当前位置: 首页 > news >正文

JavaEE企业级应用开发教程——第十二章 Spring MVC数据绑定和相应(黑马程序员第二版)(SSM)

第十二章 Spring MVC数据绑定和相应

12.1 数据绑定

在这里插入图片描述
在 Spring MVC 中,当接收到客户端的请求时,会根据请求参数和请求头等信息,将参数以特定的方式转换并绑定到处理器的形参中,这个过程称为数据绑定。数据绑定的流程大致如下:

  1. Spring MVC 接收到客户端的请求后,将请求对象 ServletRequest 传递给 DataBinder。
  2. DataBinder 将处理方法的形参对象传递进来,准备将请求消息数据填充到形参对象中。
  3. DataBinder 调用 ConversionService 组件进行数据类型转换、数据格式化等工作,将 ServletRequest 对象中的消息填充到形参对象中。
  4. DataBinder 调用 Validator 组件对已经绑定了请求消息数据的形参对象进行数据合法性校验。
  5. 校验完成后,生成数据绑定结果 BindingResult 对象。
    Spring MVC 将 BindingResult 对象中的内容赋给处理方法的相应参数,完成数据绑定。

需要注意的是,数据绑定的过程中使用了 ConversionService 和 Validator 组件。ConversionService 组件用于将请求中的参数转换为处理器中需要的参数类型,Validator 组件用于校验参数的合法性。在数据绑定过程中,还可以使用自定义的 Converter 和 Validator 对象来实现更复杂的数据转换和校验逻辑。

数据绑定是 Spring MVC 中非常重要的一个环节,它能够将客户端请求的参数和处理器方法的参数进行连接,使得处理器能够正确地获取到需要的参数,从而完成业务逻辑的处理。同时,数据绑定也能够减少开发者的工作量,提高开发效率。


12.2 简单数据的绑定

在 Spring MVC 中,简单数据绑定是指将客户端请求中的参数直接绑定到服务器端处理方法的形参或形参的属性上,而不是基于列表或多层级的数据。简单数据绑定一般分为以下三种:


12.1.1 默认类型数据绑定

Spring MVC 框架默认支持一些数据类型,当使用这些类型作为处理器的形参类型时,Spring MVC 的参数处理适配器会默认识别这些类型并进行赋值。常见的默认类型如下:

  1. HttpServletRequest:通过该类型的形参可以获取客户端请求的信息,例如请求参数、请求头等。
@RequestMapping("/submit")
public String submit(HttpServletRequest request, Model model) {// 获取请求参数String id = request.getParameter("id");String name = request.getParameter("name");// 处理业务逻辑return "result";
}

  1. HttpServletResponse:通过该类型的形参可以处理服务器端的响应信息,例如设置响应头、写入响应体等。
@RequestMapping("/submit")
public void submit(HttpServletResponse response) throws IOException {// 设置响应头response.setContentType("text/plain");// 写入响应体response.getWriter().write("success");
}

  1. HttpSession:通过该类型的形参可以获取客户端的 session 对象,从而获取 session 中存放的数据。
@RequestMapping("/submit")
public String submit(HttpSession session, Model model) {// 获取 session 中的数据User user = (User) session.getAttribute("user");// 处理业务逻辑return "result";
}

  1. Model/ModelMap:这两个类型都可以用来设置 model 数据,并将 model 数据填充到 request 域中。
@RequestMapping("/submit")
public String submit(Model model) {// 设置 model 数据model.addAttribute("id", 1);model.addAttribute("name", "Tom");// 处理业务逻辑return "result";
}
@RequestMapping("/submit")
public String submit(ModelMap model) {// 设置 model 数据model.addAttribute("id", 1);model.addAttribute("name", "Tom");// 处理业务逻辑return "result";
}

以上是常见的几种 Spring MVC 默认支持的数据类型,它们可以方便地获取请求信息、处理响应信息、获取 session 中的数据以及设置 model 数据。


案例

假设有一个处理方法如下所示:

@RequestMapping("/user")
public String getUserInfo(HttpServletRequest request, HttpServletResponse response) {// 业务逻辑
}

在上述处理方法中,我们通过 HttpServletRequest 和 HttpServletResponse 参数来获取客户端请求和向客户端发送响应。在请求处理过程中,参数处理适配器会自动识别这些参数类型,并将 HttpServletRequest 和 HttpServletResponse 对象赋值给相应的参数。

如果处理方法的参数类型不是参数处理适配器默认识别的类型,那么就需要手动编写代码进行处理。例如,当处理方法的参数类型是自定义类型时,我们需要通过自定义的转换器或者注解来将客户端请求中的参数值进行转换和赋值。


12.2.2 简单数据类型绑定

在 Spring MVC 中,简单数据类型的绑定指的是将客户端请求中的简单类型参数(例如 Integer、Double、String 等基本类型或其包装类型)自动绑定到处理方法的形参上。通常情况下,只需要保证客户端请求参数的名称和处理方法的形参名称一致即可,Spring MVC 会自动将请求参数映射并匹配到处理方法的形参上,完成数据绑定。

下面是一个示例代码:

@RequestMapping("/user")
public String getUserInfo(@RequestParam("userId") Integer userId, @RequestParam("userName") String userName) {// 处理请求参数,例如查询用户信息等// ...
}

在上述代码中,处理方法的参数类型分别为 Integer 和 String,客户端请求参数的名称分别为 “userId” 和 “userName”。由于请求参数名称和处理方法的形参名称一致,因此 Spring MVC 会自动将请求参数映射到处理方法的形参上,并完成数据绑定。

需要注意的是,如果客户端请求参数的名称和处理方法的形参名称不一致,或者请求参数的类型与处理方法的形参类型不一致,那么就需要通过 @RequestParam 注解或者其他方式来进行手动配置(一致的话可以不写)。例如,可以通过 @RequestParam 注解来指定请求参数的名称,或者通过 @ModelAttribute 注解来指定请求参数的类型和名称等。


@RequestParam

@RequestParam 注解是 Spring MVC 中常用的注解之一,用于将客户端请求参数映射到处理方法的形参上。它有以下常用属性:

属性名类型是否必填默认值描述
valueString“”指定客户端请求参数的名称,用于与处理方法的形参名称进行映射。
requiredbooleantrue指定客户端请求参数是否为必填项。如果为 true,但客户端请求中没有该参数,则会抛出异常。
defaultValueStringValueConstants.DEFAULT_NONE指定客户端请求参数的默认值。如果客户端请求中没有该参数,则会使用默认值。
nameString“”value 属性的别名。
paramTypeString“”指定客户端请求参数的类型。可以是 path、query、header、cookie 等,用于指定参数来源。

需要注意的是,value 和 name 属性等价,都用于指定客户端请求参数的名称。在使用 @RequestParam 注解时,必须指定 value 或 name 属性中的一个。如果不指定这两个属性中的任意一个,则会抛出异常。

示例代码:

@RequestMapping("/user")
public String getUserInfo(@RequestParam(value = "userId", required = true, defaultValue = "0") Integer userId, @RequestParam(name = "userName", required = true) String userName) {// 处理请求参数,例如查询用户信息等// ...
}

在上述代码中,@RequestParam 注解指定了 value 和 name 属性,用于指定客户端请求参数的名称。required 属性用于指定客户端请求参数是否为必填项,defaultValue 属性用于指定客户端请求参数的默认值。


@PathVariable

当请求的映射方式是 REST 风格时,通常会将动态参数作为 URL 的一部分,例如:
/user/123
其中,123 是动态参数,表示用户的 ID。此时,使用传统的方式将参数绑定到处理方法的形参中,不再适用,因为参数不再是请求参数,而是 URL 的一部分。


为了解决这个问题,Spring MVC 提供了 @PathVariable 注解,用于将 URL 中的占位符参数绑定到处理方法的形参中。例如,在上述例子中,我们可以通过以下方式获取用户 ID:

@RequestMapping("/user/{userId}")
public String getUserInfo(@PathVariable("userId") Integer userId) {// 处理请求参数,例如查询用户信息等// ...
}

在上述代码中,@PathVariable 注解用于将 URL 中的占位符参数 “{userId}” 绑定到处理方法的形参 “userId” 上。通过这种方式,我们可以方便地获取 URL 中的动态参数,并且不再需要通过传统的方式将参数作为请求参数绑定到处理方法中。


@PathVariable 注解是 Spring MVC 中常用的注解之一,用于将 URL 中的占位符参数绑定到处理方法的形参中。@PathVariable 注解有以下两个常用属性:

  • value:用于指定 URL 中的占位符名称,用于与处理方法的形参名称进行映射。例如,在下面的例子中,@PathVariable 注解指定了 value 属性为 “userId”,表示将 URL 中的占位符参数 “{userId}” 与处理方法的形参 “userId” 进行映射。

  • required:用于指定该占位符参数是否为必填项。如果为 true,但客户端请求中没有该参数,则会抛出异常。默认值为 true。例如,在下面的例子中,@PathVariable 注解指定了 required 属性为 false,表示该占位符参数不是必填项,如果客户端请求中没有该参数,则会将形参 “userId” 的值设置为 null。

需要注意的是,@PathVariable 注解只能用于绑定简单数据类型,例如 String、int、long 等。如果需要绑定复杂数据类型,例如自定义对象,则需要使用 @RequestParam 或 @RequestBody 注解。

如果 URL 中包含占位符参数需要在处理器方法中获取,那么通常需要使用 @PathVariable 注解来完成参数的绑定。但如果 URL 中的占位符参数名称和处理器方法的形参名称一样,那么在这种情况下,可以省略 @PathVariable 注解,Spring MVC 会自动将 URL 中的参数值绑定到对应的形参上。


12.2.3 POJO绑定

当客户端向服务器发送请求时,请求中可能会包含多个不同类型的参数数据。如果使用简单数据类型进行绑定,需要手动编写多个不同类型的参数,操作比较繁琐。为解决这个问题,可以使用 POJO 类型进行数据绑定。

POJO(Plain Old Java Object)是一个普通的 Java 对象,不依赖于特定的框架或技术。使用 POJO 类型进行数据绑定,就是将所有关联的请求参数封装在一个 POJO 对象中,然后在方法中直接使用该对象作为形参来完成数据绑定。


本案例是一个用户注册案例,要求将表单提交的数据绑定在处理器中定义的 User 类型的形参中。具体实现步骤如下:

1. 创建 User 类

首先,需要创建一个 User 类来表示用户信息。可以在该类中添加相应的属性和对应的 getter/setter 方法,例如:

public class User {private String username;private String password;private int age;public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}
}

2. 创建处理器方法

接下来,需要创建一个处理器方法来处理用户注册请求。在方法的参数列表中,直接使用 User 类型作为形参即可完成数据绑定。例如:

@RequestMapping("/register")
public String register(User user) {// 处理注册请求return "success";
}

3. 创建表单页面

在客户端需要创建一个表单页面,用于向服务器发送请求。表单中的 input 标签的 name 属性需要与 User 类中的属性名相对应。例如:

<form action="/register" method="post"><label>用户名:<input type="text" name="username"></label><br><label>密码:<input type="password" name="password"></label><br><label>年龄:<input type="text" name="age"></label><br><input type="submit" value="注册">
</form>

4. 测试 POJO 绑定

最后,启动应用程序并在浏览器中访问表单页面。在表单中填写相应的信息并提交表单后,服务器应该能够正确地接收并处理请求。

在处理器方法中,可以直接使用 User 对象来访问表单数据,例如:

@RequestMapping("/register")
public String register(User user) {String username = user.getUsername();String password = user.getPassword();int age = user.getAge();// 处理注册请求return "success";
}

使用 POJO 类型进行数据绑定可以大大简化代码的编写和维护。同时,使用 POJO 类型还可以更好地封装数据,增强代码的可读性和可维护性。


防止中文数据乱码

要防止客户端传入的中文数据出现乱码,可以采取以下措施:

  1. 在表单页面中设置字符编码
    在表单页面的 head 标签中添加以下代码,设置表单的字符编码为 utf-8:
<meta charset="utf-8">

  1. 在服务器端设置字符编码
    在服务器端的配置文件中,设置字符编码为 utf-8。例如,在 Spring MVC 中可以在 web.xml 中添加以下代码:
<filter><filter-name>encodingFilter</filter-name><filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class><init-param><param-name>encoding</param-name><param-value>UTF-8</param-value></init-param>
</filter><filter-mapping><filter-name>encodingFilter</filter-name><url-pattern>/*</url-pattern>
</filter-mapping>

  1. 在处理器方法中设置字符编码
    在处理器方法中,可以使用 @RequestMapping 注解的 produces 属性来设置响应的字符编码为 utf-8。例如:
@RequestMapping(value = "/register", produces = "text/html;charset=UTF-8")
public String register(User user) {// 处理注册请求return "success";
}

通过以上措施,可以有效地防止客户端传入的中文数据出现乱码。需要注意的是,以上措施需要同时在客户端和服务器端进行设置才能生效。


12.2.4 自定义类型转换器

在 Spring MVC 中,为了方便处理客户端提交的参数,Spring 默认提供了一些常用的类型转换器,可以将客户端提交的参数自动转换为处理器形参类型的数据。但是默认类型转换器并不能将提交的参数转换为所有的类型,此时就需要开发者自定义类型转换器。

Spring 框架提供了 org.springframework.core.convert.converter.Converter 接口作为类型转换器,开发者可以通过实现该接口来自定义类型转换器。Converter 接口定义了一个 convert 方法,用于将源类型转换为目标类型并返回。具体转换规则可以由开发者自行定义。


Converter 接口是 Spring 框架中用来定义类型转换器的接口,其代码如下所示:

public interface Converter<S, T> {/*** 将 S 类型转换为 T 类型** @param source 源类型* @return 转换后的目标类型*/T convert(S source);
}

Converter 接口中有两个泛型参数,分别代表源类型 S 和目标类型 T。接口中只有一个方法 convert,该方法接收一个 S 类型的参数 source,将其转换为一个 T 类型的对象并返回。


例子

好的,下面以实现 Date 类型的数据绑定为例,来演示自定义类型转换器的具体实现步骤。

1. 实现自定义类型转换器

首先,需要实现一个自定义类型转换器,将字符串类型的日期转换为 Date 类型的对象。具体实现步骤如下:

1.1 创建一个实现 Converter 接口的类,并指定泛型参数为 String 和 Date,代码如下:

public class StringToDateConverter implements Converter<String, Date> {// 具体实现
}

1.2 在类中重写 convert 方法,将字符串类型的日期转换为 Date 类型的对象并返回,代码如下:

public class StringToDateConverter implements Converter<String, Date> {@Overridepublic Date convert(String source) {SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");try {return dateFormat.parse(source);} catch (ParseException e) {throw new IllegalArgumentException("Invalid date format, please use yyyy-MM-dd");}}
}

在该自定义类型转换器中,使用 SimpleDateFormat 对字符串进行解析,并捕获 ParseException 异常,以便在日期格式不正确时进行提示。


2. 注册自定义类型转换器

接下来,需要将自定义类型转换器注册到 Spring 容器中,以便在数据绑定时生效。具体实现步骤如下:

2.1 创建一个配置类,实现 WebMvcConfigurer 接口,代码如下:

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {// 具体实现
}

2.2 在配置类中重写 addFormatters 方法,并在其中注册自定义类型转换器,代码如下:

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {@Overridepublic void addFormatters(FormatterRegistry registry) {registry.addConverter(new StringToDateConverter());}
}

在 addFormatters 方法中,通过调用 registry.addConverter 方法将自定义类型转换器注册到 Spring 容器中。


3. 使用自定义类型转换器

注册自定义类型转换器后,就可以在处理器中使用 Date 类型的数据了。具体实现步骤如下:

3.1 在处理器方法的参数中添加一个 Date 类型的参数,代码如下:

@PostMapping("/user/register")
public String register(User user, Date birthday) {// 处理用户注册逻辑return "success";
}

在上述处理器中,接收了一个 User 对象作为参数,同时添加了一个 Date 类型的参数 birthday。

3.2 在客户端提交参数时,使用 yyyy-MM-dd 格式的字符串表示日期,例如:

POST /user/register HTTP/1.1
Content-Type: application/x-www-form-urlencodedname=Tom&birthday=2023-04-11

在客户端提交的参数中,将日期以字符串的形式提交,使用 yyyy-MM-dd 格式。

3.3 在处理器中使用 Date 类型的参数,Spring 框架会自动调用自定义类型转换器将字符串类型的日期转换为 Date 类型的对象,代码如下:

@PostMapping("/user/register")
public String register(User user, Date birthday) {user.setBirthday(birthday);// 处理用户注册逻辑return "success";
}

在处理器中,将转换后的 Date 对象设置到 User 对象的 birthday 属性中,即可完成数据绑定。


@DateTimeFormat 是一个 Spring 提供的注解,用于指定日期类型的格式化方式。可以将它应用于 Controller 方法的参数上,以便自动将请求参数转换为指定格式的日期类型。

下面是一个示例:

@PostMapping("/save")
public String save(@RequestParam("date") @DateTimeFormat(pattern = "yyyy-MM-dd") Date date) {// 处理逻辑return "success";
}

在上述示例中,我们使用了 @DateTimeFormat 注解来指定日期类型的格式化方式,将格式化模式设置为 “yyyy-MM-dd”,表示接受的日期格式为 “年-月-日”。然后,将 @DateTimeFormat 注解应用于 Controller 方法的参数上,以便将请求参数自动转换为指定格式的日期类型。

需要注意的是,@DateTimeFormat 注解只能用于 Controller 方法的参数上,并且只能用于支持格式化的日期类型,比如 Date、Calendar 和 LocalDate 等。如果需要使用自定义的格式化方式,可以考虑实现 Formatter 接口,或者使用全局的日期格式化器。


12.3 复杂数据绑定

当我们在开发Web应用时,常常需要将请求参数绑定到Java对象上,以便进行后续的业务逻辑处理,这就是数据绑定。在Spring MVC中,简单数据绑定已经足够应对大部分的数据绑定需求,但是在实际开发中,我们还会遇到一些比较复杂的数据绑定问题,例如:

  • 数组的绑定
  • 集合的绑定
  • 复杂POJO的绑定
  • JSON数据的绑定

12.3.1 数组绑定

在Spring MVC中,我们可以使用@RequestParam注解将请求参数绑定到一个数组上。需要注意的是,当绑定数组时,请求参数的名称必须与数组名称相对应,并且请求参数的值应该用逗号分隔。

@PostMapping("/save")
public String save(@RequestParam("ids") Long[] ids) {// 处理逻辑return "success";
}

处理方法参数的类型为数组时,默认情况下会将请求参数的名称与数组名称相对应,此时可以不写@RequestParam(“ids”)


12.3.2 集合绑定

与数组类似,我们也可以使用@RequestParam注解将请求参数绑定到一个集合上。需要注意的是,当绑定集合时,请求参数的名称必须与集合名称相对应,并且请求参数的值应该用逗号分隔。

@PostMapping("/save")
public String save(@RequestParam("ids") List<Long> ids) {// 处理逻辑return "success";
}

12.3.3 复杂POJO绑定

1. 属性为对象类型的数据绑定

当客户端请求中传递的参数比较复杂时,使用简单 POJO 已经不能很好地满足需求,这时可以考虑使用复杂 POJO 类型的数据绑定。复杂 POJO 类型的数据绑定不仅包含简单数据类型,还包含对象类型、List 类型和 Map 类型等其他引用类型。


例子

下面通过一个获取用户订单信息的案例,演示复杂 POJO 中属性为对象类型的数据绑定,案例具体实现步骤如下:

  1. 定义订单类和用户类
    我们先定义一个订单类 Order 和一个用户类 User,其中订单类中包含一个用户对象。
public class Order {private Long orderId;private User user;// getter和setter方法// 对象属性绑定public User getUser() {return user;}public void setUser(User user) {this.user = user;}
}public class User {private String username;private String email;// getter和setter方法// 对象属性绑定public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getEmail() {return email;}public void setEmail(String email) {this.email = email;}
}

在订单类中,我们通过定义 getUser() 和 setUser() 方法,将订单对象中的用户属性绑定到用户对象中的相应属性上,从而实现订单和用户属性的同步更新。

  1. 编写控制器方法
    我们在控制器中定义一个方法,用于接收用户订单信息,并将信息绑定到订单对象中。

@RequestMapping("/order")
public String getOrder(Order order) {System.out.println("订单号:" + order.getOrderId());System.out.println("用户名:" + order.getUser().getUsername());System.out.println("邮箱:" + order.getUser().getEmail());return "success";
}

在控制器方法中,我们通过 @RequestMapping 注解定义一个 /order 的请求映射,然后通过 Order 类型的参数 order 接收请求中的订单信息,并输出订单号、用户名、邮箱等信息。


  1. 编写前端页面
    最后,我们编写一个前端页面,用户可以在页面中输入订单信息和用户信息,然后提交表单,将信息发送到控制器方法中进行处理。
<form action="/order" method="post"><label>订单号:</label><input type="text" name="orderId"><br><label>用户名:</label><input type="text" name="user.username"><br><label>邮箱:</label><input type="text" name="user.email"><br><input type="submit" value="提交">
</form>

在前端页面中,我们通过 标签定义一个表单,然后通过 name 属性将表单中的各个输入框与订单和用户对象中的相应属性进行绑定。最后,用户点击提交按钮后,表单将被提交到 /order 请求中,由控制器方法进行处理。

以上就是通过一个获取用户订单信息的案例,演示复杂 POJO 中属性为对象类型的数据绑定的具体实现步骤。


2. 属性为List类型的数据绑定

本案例演示了如何在订单业务中实现用户和订单的一对多关系,即一个订单对应多个用户。在实现过程中,我们使用了复杂 POJO 中属性为 List 类型的数据绑定。

具体实现步骤如下:

  1. 定义订单类和用户类
    我们先定义一个订单类 Order 和一个用户类 User,其中订单类中包含一个用户集合。
public class Order {private Long orderId;private List<User> users;// getter和setter方法// 集合属性绑定public List<User> getUsers() {return users;}public void setUsers(List<User> users) {this.users = users;}
}public class User {private String username;private String email;// getter和setter方法// 对象属性绑定public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getEmail() {return email;}public void setEmail(String email) {this.email = email;}
}

在订单类中,我们通过定义 getUsers() 和 setUsers() 方法,将订单对象中的用户集合属性绑定到用户集合对象中,从而实现订单和用户属性的同步更新。


  1. 编写控制器方法
    在控制器中定义一个方法,用于接收用户订单信息,并将信息绑定到订单对象中。
@RequestMapping("/order")
public String getOrder(Order order) {System.out.println("订单号:" + order.getOrderId());for(User user : order.getUsers()) {System.out.println("用户名:" + user.getUsername());System.out.println("邮箱:" + user.getEmail());}return "success";
}

在控制器方法中,我们通过 @RequestMapping 注解定义一个 /order 的请求映射,然后通过 Order 类型的参数 order 接收请求中的订单信息,并输出订单号、用户名、邮箱等信息。


  1. 编写前端页面
    最后,我们编写一个前端页面,用户可以在页面中输入订单信息和用户信息,然后提交表单,将信息发送到控制器方法中进行处理。
<form action="/order" method="post"><label>订单号:</label><input type="text" name="orderId"><br><h3>用户信息</h3><div id="users"><div class="user"><label>用户名:</label><input type="text" name="users[0].username"><label>邮箱:</label><input type="text" name="users[0].email"></div><div class="user"><label>用户名:</label><input type="text" name="users[1].username"><label>邮箱:</label><input type="text" name="users[1].email"></div></div><input type="submit" value="提交">
</form>

在前端页面中,我们通过 name 属性将信息绑定到 Order 类型的 order 参数中,其中用户信息使用了集合类型,并通过 users[index].username 和 users[index].email 的方式指定了用户的下标。

这样,当用户提交表单时,控制器方法就会自动将信息绑定到订单对象中,并输出订单号、用户名、邮箱等信息。


3. 属性为Map类型的数据绑定

本案例演示了如何使用复杂 POJO 中属性为 Map 类型的数据绑定,实现获取订单信息的功能。

具体实现步骤如下:

  1. 定义订单类
    我们先定义一个订单类 Order,其中包含一个 Map 类型的属性 params,用于存储订单的详细信息。
public class Order {private Long orderId;private Map<String, Object> params;// getter和setter方法// Map属性绑定public Map<String, Object> getParams() {return params;}public void setParams(Map<String, Object> params) {this.params = params;}
}

在订单类中,我们通过定义 getParams() 和 setParams() 方法,将订单对象中的 Map 类型属性绑定到 Map 类型对象中,从而实现订单属性的同步更新。


  1. 编写控制器方法
    在控制器中定义一个方法,用于接收订单信息,并将信息绑定到订单对象中。
@RequestMapping("/order")
public String getOrder(Order order) {System.out.println("订单号:" + order.getOrderId());Map<String, Object> params = order.getParams();for(Map.Entry<String, Object> entry : params.entrySet()) {System.out.println(entry.getKey() + ":" + entry.getValue());}return "success";
}

在控制器方法中,我们通过 @RequestMapping 注解定义一个 /order 的请求映射,然后通过 Order 类型的参数 order 接收请求中的订单信息,并输出订单号、订单详细信息等信息。


  1. 编写前端页面
    最后,我们编写一个前端页面,用户可以在页面中输入订单信息和详细信息,然后提交表单,将信息发送到控制器方法中进行处理。
<form action="/order" method="post"><label>订单号:</label><input type="text" name="orderId"><br><h3>订单详细信息</h3><label>收货人姓名:</label><input type="text" name="params[receiverName]"><br><label>收货地址:</label><input type="text" name="params[receiverAddress]"><br><label>收货人电话:</label><input type="text" name="params[receiverPhone]"><br><input type="submit" value="提交">
</form>

在前端页面中,我们通过表单的 name 属性,将订单号和订单详细信息绑定到 Order 对象中的属性中,其中订单详细信息使用了 Map 类型的属性 params,通过 name=“params[key]” 的方式,将详细信息的键值对绑定到 Map 中。

以上就是使用 Map 类型实现 POJO 数据绑定的完整流程。


12.3.4 JSON数据绑定

在Spring MVC中,客户端请求中发送的数据通常为JSON格式。为了将HttpServletRequest中的数据转换成指定的对象,或者将对象转换成指定格式的数据,我们需要使用对应的消息转换器来实现。Spring提供了一个HttpMessageConverter接口作为消息转换器,其中MappingJackson2HttpMessageConverter是HttpMessageConverter接口的实现类之一。

消息转换器的作用是将HTTP请求中的报文数据转换成指定对象,或者将对象转换成指定格式的报文进行响应。需要注意的是,HttpMessageConverter和之前所学习的Converter类型转换器是有区别的。Converter类型转换器用于对象之间的类型转换,而HttpMessageConverter则用于请求消息和响应消息的格式转换。

下面是一个使用MappingJackson2HttpMessageConverter将请求报文数据转换成指定对象的例子:

@Controller
@RequestMapping("/user")
public class UserController {@PostMapping("/add")@ResponseBodypublic Result addUser(@RequestBody User user) {// 处理用户添加逻辑return new Result("success", 200);}
}

在上述代码中,@RequestBody注解表示将请求报文中的数据解析成指定对象,而**@ResponseBody**注解表示将返回的对象转换成指定格式的数据进行响应。在这个例子中,MappingJackson2HttpMessageConverter会将请求报文中的JSON数据解析成User对象,并将User对象作为参数传递给addUser()方法。

需要注意的是,在使用MappingJackson2HttpMessageConverter时,需要确保Jackson库已经在项目中引入,否则会出现异常。


例子

下面通过一个异步提交商品信息的案例,来演示 Spring MVC 中的JSON 数据绑定。具体实现步骤如下:

  1. 创建一个实体类Goods,用于存储商品信息。
public class Goods {private String name;private String description;private double price;// 省略getter和setter方法
}
创建一个Controller类,用于处理请求。
@Controller
@RequestMapping("/goods")
public class GoodsController {@PostMapping("/add")@ResponseBodypublic Result addGoods(@RequestBody Goods goods) {// 处理添加商品逻辑return new Result("success", 200);}
}

  1. 在前端页面中,使用jQuery的ajax()方法,将商品信息以JSON格式提交给后端Controller。
<form><label for="name">商品名称:</label><input type="text" id="name"><br><label for="description">商品描述:</label><input type="text" id="description"><br><label for="price">商品价格:</label><input type="text" id="price"><br><button type="button" id="submit">提交</button>
</form>
<script>$(function() {$('#submit').click(function() {var data = {"name": $('#name').val(),"description": $('#description').val(),"price": $('#price').val()};$.ajax({url: '/goods/add',type: 'POST',contentType: 'application/json',data: JSON.stringify(data),success: function(result) {alert(result.msg);}});});});
</script>

在上述代码中,我们使用jQuery的ajax()方法异步提交商品信息到后端Controller。其中,contentType属性设置请求的数据格式为JSON,data属性将商品信息以JSON格式提交给后端Controller。


  1. 在Spring MVC的配置文件中,配置MappingJackson2HttpMessageConverter,以支持JSON数据的绑定。
<mvc:annotation-driven><mvc:message-converters><bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/></mvc:message-converters>
</mvc:annotation-driven>

在上述代码中,我们通过配置mvc:message-converters标签来配置MappingJackson2HttpMessageConverter,以支持JSON数据的绑定。

需要注意的是,在使用MappingJackson2HttpMessageConverter时,需要确保Jackson库已经在项目中引入,否则会出现异常。


12.4 页面跳转

客户端和服务器端之间的交互大致分为请求和响应。Spring MVC 在接收客户端的请求后,会对请求进行不同方式的响应。Spring MVC 的响应方式可以分为页面跳转数据回写两种。

当使用页面跳转的方式进行响应时,Spring MVC 提供了多种方法来指定跳转页面,包括方法的返回值可以定义为void类型、String类型和ModelAndView类型。

12.4.1 返回值为void类型的页面跳转

当Spring MVC方法的返回值为void类型时,方法执行后会跳转到默认的页面。默认的页面由方法映射路径和视图解析器中的前缀、后缀拼接而成,拼接格式为“前缀+方法映射路径+后缀”。

举个例子,如果我们有一个方法映射路径为“/index”,在视图解析器中配置了前缀为“/WEB-INF/views/”,后缀为“.jsp”,那么当这个方法返回void类型时,Spring MVC 会默认跳转到“/WEB-INF/views/index.jsp”这个页面。

需要注意的是,如果在Spring MVC的配置文件中没有配置视图解析器,则会报HTTP Status 500错误。

以下是一个视图解析器的配置示例:

在上述代码中,我们配置了一个名为viewResolver的视图解析器,将前缀设置为“/WEB-INF/views/”,将后缀设置为“.jsp”。

12.4.2 返回值为String类型的页面跳转

当Spring MVC方法的返回值为String类型时,控制器方法执行后,Spring MVC会根据方法的返回值跳转到对应的资源。如果Spring MVC的配置文件中没有配置视图解析器,处理器执行后,会将请求转发到与方法返回值一致的映射路径。

在进行页面跳转之前,可以根据需求在页面跳转时选择是否携带数据。下面分别对返回值为String类型时不携带数据页面跳转和携带数据页面跳转进行讲解。


  1. 不携带数据页面跳转
    当方法的返回值为String类型时,返回值可以直接指定要跳转的页面的名称或路径,此时不会将数据传递给页面。
@RequestMapping("/index")
public String index() {return "index";
}

在上述代码中,当请求路径为/index时,Spring MVC会返回名为index的页面。


  1. 携带数据页面跳转
    当方法的返回值为String类型时,可以通过Model来携带数据,将需要传递的数据放入Model中,然后将Model作为参数传递给方法,Spring MVC会自动将数据传递给页面。
@RequestMapping("/index")
public String index(Model model) {model.addAttribute("name", "Tom");return "index";
}

在上述代码中,我们将一个名为name的属性添加到Model中,属性值为Tom,然后将Model作为参数传递给方法,Spring MVC会自动将数据传递给名为index的页面。

需要注意的是,我们需要在页面中使用EL表达式来获取Model中的数据。

<h1>Hello, ${name}!</h1>

12.4.3 返回值为ModelAndView类型的页面跳转

除了前面讲解的使用方法返回值和Model来实现页面跳转和数据传输外,Spring MVC还提供了一个兼顾视图和数据的对象,即ModelAndView。ModelAndView对象包含视图相关内容和模型数据这两个部分。

视图相关的内容可以设置逻辑视图的名称,也可以设置具体的View实例;模型数据则会在视图渲染过程中被合并到最终的视图输出。

ModelAndView 提供了设置视图和数据模型的方法,如表所示

方法名描述
setViewName(String viewName)设置逻辑视图名称
setView(View view)设置具体的View实例
addObject(String attributeName, Object attributeValue)向模型数据中添加属性
addAllObjects(Map<String, ?> modelMap)向模型数据中添加多个属性
getModel()获取模型数据
getViewName()获取逻辑视图名称
getView()获取具体的View实例
getModelMap()获取模型数据Map
clear()清空模型数据和视图

// 将该方法映射到 "/showModelAndView" 路径下
@RequestMapping("/showModelAndView")
public ModelAndView showModelAndView() {// 创建 ModelAndView 实例ModelAndView modelAndView = new ModelAndView();// 向 ModelAndView 实例中添加名称为 "username" 的数据modelAndView.addObject("username", "heima");// 创建一个 User 对象并设置其属性User user = new User();user.setPassword("password2");// 向 ModelAndView 实例中添加名称为 "user" 的数据modelAndView.addObject("user", user);// 设置 ModelAndView 实例的视图名称为 "register"modelAndView.setViewName("register");// 返回 ModelAndView 实例return modelAndView;
}

在上述代码中,我们创建了一个名为 showModelAndView() 的方法,并将其映射到 “/showModelAndView” 路径下。在该方法中,我们创建了一个 ModelAndView 实例,并使用 addObject() 方法向其中添加了名称为 “username” 和 “user” 的两个数据。接着,我们创建了一个 User 对象并设置了其属性,并将其添加到 ModelAndView 实例中。最后,我们使用 setViewName() 方法设置了 ModelAndView 实例的视图名称为 “register”。最终,我们返回该 ModelAndView 实例。

通过以上代码,我们可以学习到如何创建 ModelAndView 实例并向其中添加数据,以及如何设置其视图名称。这些操作可以帮助我们更加灵活地控制页面的跳转和数据的显示。


12.5 数据回写

Spring MVC 默认使用视图解析器将响应数据解析为视图并进行页面跳转,但有时客户端希望服务器端在响应时不进行页面跳转,而只是返回相关的数据。这时候,我们可以选择直接将数据写入响应输出流中,而不经过视图解析器。根据数据格式,我们可以将回写到输出流的数据分为普通字符串和 JSON 数据。

1. 普通字符串的回写

如果想要回写普通字符串到输出流中,我们可以使用 HttpServletResponse 对象的 getWriter() 方法获取输出流,并将数据写入输出流中。下面是一个示例代码:

@RequestMapping("/writeString")
public void writeString(HttpServletResponse response) throws IOException {response.setContentType("text/plain;charset=UTF-8");PrintWriter writer = response.getWriter();writer.write("Hello, world!");writer.flush();writer.close();
}

在上述代码中,我们创建了一个名为 writeString() 的方法,并将其映射到 /writeString 路径下。在该方法中,我们首先使用 setContentType() 方法设置响应的内容类型为 “text/plain;charset=UTF-8”,然后使用 getWriter() 方法获取输出流,并将字符串 “Hello, world!” 写入输出流中。最后,我们使用 flush() 方法刷新输出流,并关闭输出流。


2. JSON 数据的回写

如果想要回写 JSON 数据到输出流中,我们可以使用 Spring MVC 提供的 @ResponseBody 注解,该注解会将返回值序列化为 JSON 数据并写入响应输出流中。下面是一个示例代码:

@RequestMapping("/writeJson")
@ResponseBody
public Map<String, Object> writeJson() {Map<String, Object> resultMap = new HashMap<>();resultMap.put("name", "Tom");resultMap.put("age", 18);return resultMap;
}

在上述代码中,我们创建了一个名为 writeJson() 的方法,并将其映射到 /writeJson 路径下。在该方法中,我们创建了一个 Map 对象,并向其中添加了两个键值对。最后,我们使用 @ResponseBody 注解将该 Map 对象序列化为 JSON 数据并写入响应输出流中。

通过以上代码,我们可以学习到如何将普通字符串和 JSON 数据回写到响应输出流中,这对于客户端请求数据时不需要页面跳转的场景非常有用。


相关文章:

JavaEE企业级应用开发教程——第十二章 Spring MVC数据绑定和相应(黑马程序员第二版)(SSM)

第十二章 Spring MVC数据绑定和相应 12.1 数据绑定 在 Spring MVC 中&#xff0c;当接收到客户端的请求时&#xff0c;会根据请求参数和请求头等信息&#xff0c;将参数以特定的方式转换并绑定到处理器的形参中&#xff0c;这个过程称为数据绑定。数据绑定的流程大致如下&…...

银行数字化转型导师坚鹏:金融数据治理、数据安全政策解读

金融数据治理、数据安全政策解读及大数据应用课程背景&#xff1a; 很多银行存在以下问题&#xff1a;不知道如何准确理解金融数据治理及数据安全相关政策不清楚金融数据治理及数据安全相关政策对银行有什么影响&#xff1f;不清楚如何有效应用金融数据治理及数据安全相关…...

Vue动图数据表格,根据字段是否为空,控制表格列的隐藏和显示

所在前面的话&#xff0c;我是个前端小白&#xff0c;大佬请绕行&#xff0c;可能大佬觉得很简单&#xff0c;但是我真的花了好几个小时去解决&#xff0c;所以记录一下&#xff0c;下次也可以作为参考。 我主要是以第二种方式进行修改的 开门见山 简述问题&#xff1a;大家…...

带你们偷瞄编程绕不开的C语言(二)

&#x1f929;&#xff1a;大家好&#xff0c;我是paperjie&#xff0c;感谢你阅读本文&#xff0c;欢迎一建三连哦。 &#x1f970;&#xff1a;这里是C专栏&#xff0c;笔者用重金(时间和精力)打造&#xff0c;基础知识一网打尽&#xff0c;希望可以帮到读者们哦。 &#x1f…...

TCP三次握手和四次挥手

文章目录TCP三次握手TCP四次挥手TCP三次握手 序列号&#xff1a;建立连接时计算机随机生成的随机数作为初始值&#xff0c;通过SYN包传给接收端主机&#xff0c;每发送一次数据就累加一次该数据字节数的大小。用来解决网络包乱序问题。 确认应答号&#xff1a;指下一次期望收到…...

L1-016 查验身份证

L1-016 查验身份证 题目链接 题意 判断18位身份证号码&#xff08;17位数字&#xff0b;1位校验码&#xff09;是否合法&#xff0c;对于不合法的身份证号码进行输出&#xff0c;若全都符合&#xff0c;则输出“All passed”&#xff0c;判断是否合法的规则如下&#xff1a; …...

强大到让人无法想象的ChatGPT-5即将发布,上千名人士却紧急叫停

目录 【ChatGPT 5简介】 【ChatGPT 5的潜在应用】 【ChatGPT 5的潜在危险】 ChatGPT4还没有好好体验&#xff0c;比GPT4强大1000倍的ChatGPT5又即将发布&#xff01;届时将彻底改变人工智能领域&#xff0c;并改变我们现有的世界 【ChatGPT 5简介】 OpenAI计划在2023年12月发…...

C++中的功能 及 用法

参考资料&#xff1a; C中&的功能 及 用法 - konglingbin - 博客园 (cnblogs.com) 对于习惯使用C进行开发的朋友们&#xff0c;在看到c中出现的&符号&#xff0c;可能会犯迷糊&#xff0c;因为在C语言中这个符号表示了取地址符&#xff0c;但是在C中它却有着不同的用途…...

Linux解除指定端口占用进程教程

Linux 解除指定端口占用进程教程 在 Linux 系统中&#xff0c;经常会遇到某个端口被占用的情况&#xff0c;这会导致某些服务无法正常运行。为了解决这个问题&#xff0c;我们需要找到占用该端口的进程&#xff0c;并将其停止。本文将介绍 Linux 中如何解除指定端口占用进程的方…...

雪花算法简介

一&#xff1a;概述 - SnowFlake 算法 - 是 Twitter 开源的分布式 id 生成算法。 - 应用场景 - 高性能的产生不重复 ID&#xff0c;支持集群的横向扩展。 二&#xff1a;原理 - 其核心思想就是&#xff1a; - 使用一个 64 bit 的 long 型的数字作为全局唯一 id。 - 在分布…...

人口普查数据集独热编码转换

人口普查数据集独热编码转换 描述 在机器学习中&#xff0c;数据的表示方式对于模型算法的性能影响很大&#xff0c;寻找数据最佳表示的过程被称为“特征工程”&#xff0c;在实际应用中许多特征并非连续的数值&#xff0c;比如国籍、学历、性别、肤色等&#xff0c;这些特征…...

牛客过第二遍

1、spring事务管理 1.1 Spring事务管理 声明式事务&#xff1a; 1 通过XML配置&#xff0c;声明某方法的事务特征 2、通过注解&#xff0c;声明某方法的事务特征&#xff0c;注解Transactional 1.2 Transactional 注解参数讲解 隔离级别传播行为回滚规则是否只读事务超时…...

科普:java与JavaScript的区别

Java和JavaScript是两种非常流行的编程语言&#xff0c;它们都有自己独特的特点和用途。尽管它们的名称相似&#xff0c;但实际上它们之间存在很多差异。在本文中&#xff0c;我们将详细介绍Java和JavaScript之间的区别。 一、Java和JavaScript的历史 Java是由Sun Microsyste…...

【教程】Unity 与 Simence PLC 联动通讯

开发平台&#xff1a;Unity 2021 依赖DLL&#xff1a;S7.NET 编程语言&#xff1a;CSharp 6.0 以上   一、前言 Unity 涉及应用行业广泛。在工业方向有着一定方向的涉足与深入。除构建数据看板等内容&#xff0c;也会有模拟物理设备进行虚拟孪生的需求需要解决。而 SIMATIC&a…...

视频编解码(一)之virtio-gpu环境搭建

一、基础概念 VA-API Video Acceleration API 是一组开源应用API接口&#xff0c;赋能应用&#xff08;比如VLC播放器、GStreamer等&#xff09;使用hardware video acceleration&#xff08;一般是GPU提供硬件视频加速功能&#xff09;&#xff0c;VA-API主要由开源库libva和…...

JDBC概述三(批处理+事务操作+数据库连接池)

一&#xff08;批处理&#xff09; 1.1 批处理简介 批处理&#xff0c;简而言之就是一次性执行多条SQL语句&#xff0c;在一定程度上可以提升执行SQL语句的速率。批处理可以通过使用Java的Statement和PreparedStatement来完成&#xff0c;因为这两个语句提供了用于处理批处理…...

MappedByteBuffer 详解(图解+秒懂+史上最全)

背景&#xff1a; 在尼恩视频版本里&#xff0c;从架构师视角&#xff0c;尼恩为大家彻底介绍 rocketmq 高可用、高并发中间件的原理与实操。 给大家底层的解读清楚 rocketmq 架构设计、源码设计、工业级高可用实操&#xff0c;含好多复杂度非常高、又非常核心的概念&#xff…...

顶点程序经典案例——树木生长

树木生长Shader一、介绍 大家好&#xff0c;我是阿赵。这次来做一个树木生长的Shader。 顶点程序作为整个渲染管线里面和片段程序并列的两大可控过程之一&#xff0c;一直存在感都比较低。我们平时制作的效果&#xff0c;很多都是在片段程序里面实现的计算&#xff0c;顶点程序…...

在云计算环境下保护Java应用程序的有效措施

云计算&#xff08;Cloud&#xff09;技术是近年来计算机科学的一个重要突破。大多数组织已经通过将自己的应用程序移入云平台而获益。不过&#xff0c;如何保证应用程序在第三方服务器上的安全性&#xff0c;是一项艰巨的挑战。 在本文中&#xff0c;我们将重点讨论Java&…...

vscode-markdown-代码片段及快捷键设置

代码片段及快捷键设置 主要为了插入表格和图片标签节约一点输入时间 代码片段设置 ctrlshiftp 打开面板输入 configure user snippets选择markdowncopy如下设置放入{}中 "tb4*4": {"prefix": "tb4*4","body": ["| $1 | $2 | $…...

ModelNet40数据集

跑PointNet,modelnet40数据集时; 有些人直接用.off文件;——【CAD模型】普林斯顿形状Banchmark中的.off文件遵循以下标准&#xff1a; OFF文件全是以OFF关键字开始的ASCII文件。下一行说明顶点的数量、面片的数量、边的数量。 边的数量可以安全地省略。对模型不会有影响(可以为…...

【都2023年了,还在问网络安全怎么入门】

前言 【都2023年了&#xff0c;还在问网络安全怎么入门】所以这一期就出一一个怎么学习网络安全的学习路线和方法&#xff0c;觉得有用的话点赞收藏下 首先咱们聊聊&#xff0c;学习网络安全方向通常会有哪些问题 1、打基础时间太长 学基础花费很长时间&#xff0c;光语言都有…...

Apple Xcode 14.3 (14E222b) 正式版发布下载

Command Line Tools for Xcode 14, tvOS 16 & watchOS 9 Simulator Runtime 请访问原文链接&#xff1a;https://sysin.org/blog/apple-xcode-14/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;sysin.org Xcode 14 包含了在所有 Ap…...

【Linux】sar常用选项介绍

sar 使用 安装sysstat apt-get install sysstat -y #或 yum install sysstat -y选项 用法: sar [ 选项 ] [ <时间间隔> [ <次数> ] ] 选项&#xff1a; [ -A ] [ -B ] [ -b ] [ -C ] [ -D ] [ -d ] [ -F [ MOUNT ] ] [ -H ] [ -h ] [ -p ] [ -q ] [ -r [ ALL ] ]…...

PHP 单笔转账到支付宝账户,支付宝公钥证书实现版本

支付宝某些业务只能使用公钥证书方式来验签 如&#xff1a;即使转账 红包等 笔者就要实现这样一个功能&#xff0c;【单笔转账到支付宝账户】&#xff0c;采用支付宝公钥证书签名来实现。 话不多说&#xff0c;流程先走起 第一步&#xff1a;下载支付宝秘钥生成器 由于我们使…...

第十四届蓝桥杯大赛软件赛省赛 C/C++ 大学 A 组 E 题

颜色平衡树问题描述格式输入格式输出样例输入样例输出评测用例规模与约定解析参考程序问题描述 格式输入 输入的第一行包含一个整数 n &#xff0c;表示树的结点数。 接下来 n 行&#xff0c;每行包含两个整数 Ci , Fi&#xff0c;用一个空格分隔&#xff0c;表示第 i 个结点 …...

Python 小型项目大全 21~25

二十一、DNA 可视化 原文&#xff1a;http://inventwithpython.com/bigbookpython/project21.html 脱氧核糖核酸是一种微小的分子&#xff0c;存在于我们身体的每个细胞中&#xff0c;包含着我们身体如何生长的蓝图。它看起来像一对核苷酸分子的双螺旋结构&#xff1a;鸟嘌呤、…...

MinIO从信息泄漏到RCE

文章目录信息泄露漏洞利用漏洞分析漏洞修复RCE漏洞分析参考文章信息泄露 漏洞利用 如果MinIO以集群方式部署&#xff0c;存在信息泄露漏洞&#xff0c;攻击者可以通过HTTP请求获取目标进程的所有环境变量&#xff0c;包括MINIO_SECRET_KEY和MINIO_ROOT_PASSWORD. vulhub有环…...

202.Spark(九):SparkStreaming案例实操

目录 一、启动zookeeper,kafka基础环境 二、项目导好jar包,并且创建源数据,并在kafka中测试能否消费到数据...

GlusterFS(GFS)分布式文件系统

目录 一.文件系统简介 1.文件系统的组成 2.文件系统的作用 3.文件系统的挂载使用 二.GlusterFS概述 1.GlusterFS是什么&#xff1f; 2.GlusterFS的特点 3.GlusterFS术语介绍 3.1 Brick&#xff08;存储块&#xff09; 3.2 Volume&#xff08;逻辑卷&#xff09; 3.3…...