Spring MVC数据绑定和响应 你了解多少?

数据绑定的概念

在程序运行时,Spring MVC接收到客户端的请求后,会根据客户端请求的参数和请求头等数据信息,将参数以特定的方式转换并绑定到处理器的形参中。Spring MVC中将请求消息数据与处理器的形参建立连接的过程就是Spring MVC的数据绑定。

Spring MVC数据绑定的过程图

SpringMVC数据绑定中的信息处理过程的步骤描述如下:

(1)Spring MVC将ServletRequest对象传递给DataBinder。

(2)将处理方法的入参对象传递给DataBinder。

(3)DataBinder调用ConversionService组件进行数据类型转换、数据格式化等工作,并将ServletRequest对象中的消息填充到参数对象中。

(4)调用Validator组件对已经绑定了请求消息数据的参数对象进行数据合法性校验。

(5)校验完成后会生成数据绑定结果BindingResult对象,Spring MVC会将BindingResult对象中的内容赋给处理方法的相应参数。

简单数据绑定

1 默认类型数据绑定

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

• HttpServletRequest:通过request对象获取请求信息。

• HttpServletResponse:通过response处理响应信息。

• HttpSession:通过session对象得到session中存放的对象。

• Model/ModelMap:Model是一个接口,ModelMap是一个类,Model的实现类对象和ModelMap对象都可以设置model数据,model数据会填充到request域。

java 复制代码
@Controller
public class UserController {
    @RequestMapping("/getUserId")
    public void getUserId(HttpServletRequest request){
        String userid= request.getParameter("userid");
        System.out.println("userid="+userid);
    }
}

通过http://localhost:8080/FirstMVCDemo/getUserId?userid=1 访问,结果如下:

2 简单数据类型绑定

简单数据类型的绑定,就是指Java中基本类型(如int、double、String等)的数据绑定。在Spring MVC中进行简单类型的数据绑定,只需客户端请求参数的名称和处理器的形参名称一致即可,请求参数会自动映射并匹配到处理器的形参完成数据绑定。

java 复制代码
@RequestMapping("/getUserNameAndId")
public void getUserNameAndId(String username, Integer id) {
    System.out.println("username=" + username + ",id=" + id);
}

通过 http://localhost:8080/FirstMVCDemo/getUserNameAndId?username=Spring\&id=1 访问:

参数别名的设置

需要注意的是,有时候客户端请求中参数名称和处理器的形参名称不一致,这就会导致处理器无法正确绑定并接收到客户端请求中的参数。为此,Spring MVC提供了@RequestParam注解来定义参数的别名,完成请求参数名称和处理器的形参名称不一致时的数据绑定。

@RequestParam注解

属性 说明
value name属性的别名,这里指参数的名称,即入参的请求参数名称,如value="name"表示请求的参数中,名称为name的参数的值将传入。如果当前@RequestParam注解只使用vaule属性,则可以省略value属性名,如@RequestParam("name")
name 指定请求头绑定的名称
required 用于指定参数是否必须,默认是true,表示请求中一定要有相应的参数
defaultValue 形参的默认值,表示如果请求中没有同名参数时的默认值
java 复制代码
@RequestMapping("/getUserName")
public void getUserName(@RequestParam(value="name",required = false,defaultValue = "lq") String username) {
       System.out.println("username = "+username);
}

访问 http://localhost:8080/FirstMVCDemo/getUserName,结果为默认username值lq

访问 http://localhost:8080/FirstMVCDemo/getUserName?name=xiaoyan,结果如下:

@RequestParam注解的value属性,给getUserName()方法中的username形参定义了别名name。此时,客户端请求中名称为name的参数,就会绑定到getUserName()方法中的username形参上。@RequestParam注解的required属性设定了请求的name参数不是必须的,如果访问时没有携带name参数,会将defaultValue属性设定的值赋给形参username。

@PathVariable注解

当请求的映射方式是REST风格时,上述对简单类型数据绑定的方式就不适用了。为此,Spring MVC提供了@PathVariable注解,通过@PathVariable注解可以将URL中占位符参数绑定到处理器的形参中。@PathVariable注解有以下两个常用属性。

• value:用于指定URL中占位符名称。

• required:是否必须提供占位符,默认值为true。

java 复制代码
//通过@PathVariable注解的value属性将占位符参数"name"和处理方法的参数username进行绑定
@RequestMapping("/user/{name}")
public void getPathVariable(@PathVariable(value = "name") String username){
    System.out.println("username="+username);
}

访问方式:http://localhost:8080/FirstMVCDemo/user/xiaoyan

从运行结果的打印信息可以看出,控制台打印出了username的值为xiaoyan。这表明访问地址后执行了getPathVariable()方法,@PathVariable注解成功将请求URL中的变量user映射到了方法的形参username上。如果请求路径中占位符的参数名称和方法形参名称一致,那么@PathVariable注解的value属性可以省略。

java 复制代码
 @RequestMapping("/user/{name}")
    public void getPathVariable(@PathVariable String name){
        System.out.println("username="+name);
    }

3 POJO绑定

POJO数据绑定的使用场景

在使用简单数据类型绑定时,可以很容易的根据具体需求来定义方法中的形参类型和个数,然而在实际应用中,客户端请求可能会传递多个不同类型的参数数据,如果还使用简单数据类型进行绑定,那么就需要手动编写多个不同类型的参数,这种操作显然比较繁琐。为解决这个问题,可以使用POJO类型进行数据绑定。

POJO类型的数据绑定就是将所有关联的请求参数封装在一个POJO中,然后在方法中直接使用该POJO作为形参来完成数据绑定。

java 复制代码
 // 接收表单用户信息
@RequestMapping("/registerUser")
public void registerUser(User user) {
    String username = user.getUsername();
    String password = user.getPassword();
    System.out.println("username="+username+",password="+password);
}

register.jsp如下(注意register.jsp创建的地方在webapp下,而不是WEB-INF下)

java 复制代码
<%--防止页码中文乱码--%>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>注册</title></head>
<body>
<form action="${pageContext.request.contextPath}/registerUser" method="post">
    用户名:<input type="text" name="username"/><br/>
    密&nbsp;&nbsp;&nbsp;码:<input type="password" name="password"/><br/>
    <input type="submit" value="注册"/>
</form>
</body>
</html>

文件目录如下:

访问URL:http://localhost:8080/FirstMVCDemo/register.jsp

页面展示如下:

在register.jsp所示页面的表单中,分别填写注册的用户名为你的名字"张三",密码为"123",然后单击"注册"按钮即可完成注册数据的提交。当单击"注册"按钮后,控制台打印信息如图所示。

从图中可以看出,程序成功打印出了用户名和密码。这表明registerUser()方法获取到了客户端请求中的参数username和参数password的值,并将username和password的值分别赋给了getUserNameAndId( )方法中user形参的username属性和password属性,实现了POJO数据绑定。

解决请求参数中的中文乱码问题

为了防止客户端传入的中文数据出现乱码,可以使用Spring提供的编码过滤器来统一编码。要使用编码过滤器,只需要在web.xml中添加如下代码。

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>
       <init-param>
           <param-name>forceEncoding</param-name>
           <param-value>true</param-value>
       </init-param>
   </filter>
   <filter-mapping>
       <filter-name>encodingFilter</filter-name>
       <url-pattern>/*</url-pattern>
   </filter-mapping>

上述代码中,在<filter>元素中,首先使用<fillter-class>元素配置了编码过滤器类org.springframework.web.filter.CharacterEncodingFilter,然后使用<init-param>元素设置统一的编码为UTF-8。最后配置<filter-mapping>元素,拦截前端页面中的所有请求,并交由名称为CharacterEncodingFilter的编码过滤器类进行处理,将所有的请求信息内容以UTF-8的编码格式进行解析。

注意:以上可以解决post请求乱码问题,对于get请求中文参数出现乱码,可以在使用参数之前重新编码,如String username = new String(user.getUsername().getBytes("ISO8859-1"),"UTF-8");,其中ISO8859-1是Tomcat默认编码,需要将Tomcat编码后的内容再按UTF-8编码。

复杂数据绑定

在实际开发中,可能会遇到客户端请求需要传递多个同名参数到服务器端的情况,这种情况采用前面讲解的简单数据绑定的方式显然是不合适的。此时,可以使用数组来接收客户端的请求参数,完成数据绑定。

数组绑定

创建一个商品类Product:

java 复制代码
package com.lq.pojo;

/**
 * @Author: Luqing Teacher
 * @CreateTime: 2025-03-09
 * @Description:
 * @Version: 1.0
 */


public class Product {
    private String proId;		//商品id
    private String proName;	//商品名称

   //get set方法自己实现
}

创建一个提交商品页面products.jsp(直接创建文件并复制内容到webapp下)

XML 复制代码
<%--
  Created by IntelliJ IDEA.
  User: lenovo
  Date: 2025/3/9
  Time: 16:23
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" %>
<html>
<head><title>提交商品</title></head>
<body>
<form action="${pageContext.request.contextPath }/getProducts" method="post">
    <table width="220px" border="1">
        <tr>
            <td>选择</td>
            <td>商品名称</td>
        </tr>
        <tr>
            <td>
                <input name="proIds" value="1" type="checkbox">
            </td>
            <td>Spring框架实战</td>
        </tr>
        <tr>
            <td>
                <input name="proIds" value="2" type="checkbox">
            </td>
            <td>SpringMVC框架实战</td>
        </tr>
        <tr>
            <td>
                <input name="proIds" value="3" type="checkbox">
            </td>
            <td>SSM框架实战</td>
        </tr>
    </table>
    <input type="submit" value="提交商品"/>
</form>
</body>
</html>

创建一个商品处理器类ProductController

java 复制代码
@Controller
public class ProductController {
    // 获取商品列表
    @RequestMapping("/getProducts")
    public void getProducts(String[] proIds) {
        for (String proId : proIds) {
            System.out.println("获取到了Id为"+proId+"的商品");	}
    }
}

访问URL:http://localhost:8080/FirstMVCDemo/products.jsp

勾选products.jsp显示效果图中所示的全部复选框,然后单击"提交商品"按钮,控制台打印信息如下图所示:

集合绑定

集合中存储简单类型数据时,数据的绑定规则和数组的绑定规则相似,需要请求参数名称与处理器的形参名称保持一致。不同的是,使用集合绑定时,处理器的形参名称需要使用@RequestParam注解标注。

接下来使用集合数据绑定来批量提交商品案例,具体实现步骤如下所示。在ProductController.java类中创建getProductList(​方法,让getProductList(​方法使用List类型来接受客户端的请求参数,具体代码如下所示:

java 复制代码
// 获取商品列表(使用List绑定数据)
@RequestMapping("/getProductList")
public void getProductList(@RequestParam("proIds") List<String> proIds) {
    for (String proId : proIds) {
        System.out.println("获取到了Id为" + proId + "的商品");
    }
}

访问:http://localhost:8080/FirstMVCDemo/products.jsp

注意:@RequestParam注解解决集合绑定的异常问题

如果getProductList( )方法中不使用@RequestParam注解,Spring MVC默认将List作为对象处理,赋值前先创建List对象,然后将proIds作为List对象的属性进行处理。由于List是接口,无法创建对象,所以会出现无法找到构造方法异常。如果将类型更改为可创建对象的类型,如ArrayList,可以创建ArrayList对象,但ArrayList对象依旧没有proIds属性,因此无法正常绑定,数据为空。此时需要告知Spring MVC的处理器proIds是一组数据, 而不是一个单一数据。通过@RequestParam注解,将参数打包成参数数组或集合后,Spring MVC才能识别该数据格式,并判定形参类型是否为数组或集合,并按数组或集合对象的形式操作数据。

复杂POJO绑定

使用简单POJO类型已经可以完成多数的数据绑定,但有时客户端请求中传递的参数比较复杂。例如,在用户查询订单时,页面传递的参数可能包括订单编号、用户名称等信息,这就包含了订单和用户两个对象的信息。如果将订单和用户的所有查询条件都封装在一个简单POJO中,显然会比较混乱,这时可以考虑使用复杂POJO类型的数据绑定。

所谓的复杂POJO,就是POJO属性的类型不止包含简单数据类型,还包含对象类型、List类型和Map类型等其他引用类型。接下来分别对复杂POJO中属性为对象类型的数据绑定、属性为List类型的数据绑定和属性为Map类型的数据绑定进行讲解

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

创建一个订单类Order,用于封装订单信息

java 复制代码
public class Order {
    private String orderId;

    public String getOrderId() {
        return orderId;
    }

    public void setOrderId(String orderId) {
        this.orderId = orderId;
    }
}

修改User.java类,在User类中新增Order类型的属性order,并定义相应的getter和setter方法。修改后User类的具体代码如下所示。

java 复制代码
public class User {

    private Integer id;
    private String username;

    private String password;

    private Order order;		//订单

    //get set方法自己实现
}

在UserController.java类中定义方法findOrderWithUser( ),用于获取客户端请求中的User信息,findOrderWithUser( )方法的具体代码如下所示。

java 复制代码
 @RequestMapping("/findOrderWithUser")
public void findOrderWithUser(User user) {
    String username = user.getUsername();
    String orderId = user.getOrder().getOrderId();
    System.out.println("username="+username+",orderId="+orderId);
}

在项目的src\main\webapp目录下,创建一个订单信息文件order.jsp,在order.jsp文件中创建一个表单,表单中包含用户名和订单编号。表单提交时将用户名和订单编号信息发送到处理器。order.jsp的具体代码如下所示:

XML 复制代码
<%@ page language="java" contentType="text/html;
	charset=UTF-8" pageEncoding="UTF-8" %>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>订单信息</title></head>
<body>
<form action="${pageContext.request.contextPath}/findOrderWithUser" method="post">
    所属用户:<input type="text" name="username"/><br/>
    订单编号:<input type="text" name="order.orderId"/><br/>
    <input type="submit" value="查询"/>
</form>
</body>
</html>

在复杂POJO数据绑定时,如果数据需要绑定到POJO属性对象的属性中,客户端请求的参数名(本例中指form表单内各元素name的属性值)的格式必须为"属性对象名称​.​属性​",其中"属性对象名称"要和POJO的属性对象名称一致,"属性"要和属性对象所属类的属性一致。

分别在输入框中输入:哪吒手办 1234

属性为List类型的数据绑定

一般订单业务中,用户和订单基本都是一对多的映射关系,即用户的订单属性使用集合类型。接下来通过一个获取用户订单信息的例子,演示复杂POJO中属性为List类型的数据绑定,案例具体实现步骤如下。修改User.java类,将User类中订单属性修改为List类型。由于用户一般拥有多个收货地址,在User类中新增List类型的地址属性。

java 复制代码
    private List<Order> orders;		//用户订单
    private List<String> address;		//订单地址

创建一个订单处理器类OrderController,在OrderController类中定义showOrders( ) 方法,用于展示用户的订单信息。

java 复制代码
@Controller
public class OrderController {//  获取用户中的订单信息

    @RequestMapping("/showOrders")
    public void showOrders(User user) {
        List<Order> orders = user.getOrders();
        List<String> addressList = user.getAddress();
        System.out.println("订单:");
        for (int i = 0; i < orders.size(); i++) {
            Order order = orders.get(i);
            String address = addressList.get(i);
            System.out.println("订单Id:" + order.getOrderId());
            System.out.println("订单配送地址:" + address);
        }
    }
}

在项目的src\main\webapp目录下,创建一个订单信息文件orders.jsp,在orders.jsp中创建一个表单用于提交用户的订单信息。表单提交时,表单数据分别封装到User的订单属性orders和地址属性address中。orders.jsp的具体代码如下所示

XML 复制代码
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<html>
<head><title>订单信息</title></head>
<body>
<form action="${pageContext.request.contextPath }/showOrders" method="post">
    <table width="220px" border="1"><!-- 下面只展示一条数据-->
        <tr>
            <td>订单号</td>
            <td>订单名称</td>
            <td>配送地址</td>
        </tr>
        <tr>
            <td><input name="orders[0].orderId" value="1" type="text"></td>
            <td><input name="orders[0].orderName" value="Java基础教程" type="text"></td>
            <td><input name="address" value="北京海淀" type="text"></td>
        </tr>
    </table>
    <input type="submit" value="订单信息"/>
</form>
</body>
</html>

查看结果:

属性为Map类型的数据绑定

接下来,通过一个获取订单信息的案例,演示复杂POJO中属性为Map类型的数据绑定,具体实现如下。修改Order.java类,在Order类中新增HashMap类型的属性productInfo,用于封装订单中的商品信息,其中productInfo的键用来存放商品的类别,productInfo的值用来存放商品类别对应的商品。

java 复制代码
private HashMap<String,Product> productInfo;	//商品信息

public HashMap<String, Product> getProductInfo() {
    return productInfo;
}

public void setProductInfo(HashMap<String, Product> productInfo) {
    this.productInfo = productInfo;
}

修改OrderController.java类,在OrderController类中新增getOrderInfo()方法,用于获取客户端提交的订单信息,并将获取到的订单信息打印在控制台。getOrderInfo()方法的具体代码如下所示:

java 复制代码
  @RequestMapping("/orderInfo")
    public void getOrderInfo(Order order) {
        String orderId = order.getOrderId();        //获取订单id
        //获取商品信息
        HashMap<String, Product> orderInfo = order.getProductInfo();
        Set<String> keys = orderInfo.keySet();
        System.out.println("订单id:" + orderId);
        System.out.println("订单商品信息:");
        for (String key : keys) {
            Product product = orderInfo.get(key);
            String proId = product.getProId();
            String proName = product.getProName();
            System.out.println(key + "类~" + "商品id:" + proId + ",商品名称:" + proName);
        }
    }

在项目的src\main\webapp目录下,创建一个订单信息页面order_info.jsp,在order_info.jsp中创建一个表单用于提交订单信息。表单提交时,表单数据分别封装到Order的orderId属性和商品信息属性productInfo中。

java 复制代码
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!-- 只展示了form表单的内容,和一条标签内容-->
<form action="${pageContext.request.contextPath}/orderInfo" method="post">
    <table border="1">
        <tr>
            <td colspan="2">
                订单id:<input type="text" name="orderId" value="1"></td>
        </tr>
        <tr>
            <td>商品Id</td>
            <td>商品名称</td>
        </tr>
        <tr>
            <td><input name="productInfo['生鲜'].proId" value="1" type="text"></td>
            <td><input name="productInfo['生鲜'].proName" value="三文鱼" type="text"></td>
        </tr>
        <tr></tr>
    </table>
    <input type="submit" value="提交"/>
</form>

页面如下,点击提交:

结果如下:

注意:数据绑定到Map类型的属性时的参数命名要求

在复杂POJO数据绑定时,如果数据绑定到Map类型的属性,客户端请求的参数名称(本例中指form表单内各元素name的属性值)必须与POJO类的层次结构名称保持一致,并使用键值的映射格式描述对象在Map中的位置,即客户端参数名称必须和要绑定的Map中的具体对象的具体属性的名称保持一致。

相关推荐
专注API从业者1 小时前
Open Claw 京东商品监控选品实战:一键抓取、实时监控、高效选品
java·服务器·数据库
摇滚侠1 小时前
DBeaver 导入数据库 导入 SQL 文件 MySQL 备份恢复
java·数据库·mysql
keep one's resolveY1 小时前
SpringBoot实现重试机制的四种方案
java·spring boot·后端
天空属于哈夫克32 小时前
企业微信API常见的错误和解决方案
java·数据库·企业微信
摇滚侠3 小时前
VMvare 虚拟机 Oracle19c 安装步骤,远程连接 Oracle19c,百度网盘安装包
java·oracle
梁萌3 小时前
idea报错找不到XX包的解决方法
java·intellij-idea·启动报错·缺少包
Agent产品评测局3 小时前
生产排期与MES/ERP系统打通,实操方法详解 —— 2026企业级智能体自动化选型与实战指南
java·运维·人工智能·ai·chatgpt·自动化
阿丰资源3 小时前
基于Spring Boot的电影城管理系统(直接运行)
java·spring boot·后端
呱牛do it3 小时前
企业级门户网站设计与实现:基于SpringBoot + Vue3的全栈解决方案(Day 8)
java
消失的旧时光-19434 小时前
Spring Boot 工程化进阶:统一返回 + 全局异常 + AOP 通用工具包
java·spring boot·后端·aop·自定义注解