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> {

    @Override
    public 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 {

    @Override
    public 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-urlencoded

name=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")
    @ResponseBody
    public 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")
    @ResponseBody
    public 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 数据回写到响应输出流中,这对于客户端请求数据时不需要页面跳转的场景非常有用。


  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值