SpringMVC请求参数的绑定
一、什么是 SpringMVC 请求参数绑定?
在 Web 开发中,表单提交、URL 传参等请求数据都是 k=v 格式(例如 username=haha&password=123)。SpringMVC 的参数绑定 就是将这些请求数据自动映射到控制器方法的参数中,无需手动解析 Request 对象,极大简化了数据获取流程。
核心绑定规则
- 表单 / 请求参数的 name 属性必须与控制器方法参数名、JavaBean 属性名一致(区分大小写)。
- 支持自动类型转换(如字符串转基本数据类型),复杂场景可自定义转换器。
二、支持的绑定数据类型
SpringMVC 支持 3 大类数据绑定,覆盖绝大多数开发场景:
- 基本数据类型 + 字符串类型(int、String、Double 等)
- 实体类型(JavaBean)及嵌套引用类型
- 集合类型(List、Map 等)
下面通过「代码实例 + 场景说明」逐一拆解:
1. 环境准备(通用配置)
首先需要完成 SpringMVC 基础配置,确保注解生效:
- web.xml:配置前端控制器、中文乱码过滤器(后续详细说明)
- springmvc.xml:开启注解扫描、配置视图解析器
<!-- springmvc.xml 核心配置 --><beans xmlns="http://www.springframework.org/schema/beans"xmlns:mvc="http://www.springframework.org/schema/mvc"xmlns:context="http://www.springframework.org/schema/context"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/mvchttp://www.springframework.org/schema/mvc/spring-mvc.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><!-- 注解扫描:扫描控制器所在包 --><context:component-scan base-package="cn.tx.demo2" /><!-- 视图解析器:跳转页面路径拼接 --><bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"><property name="prefix" value="/WEB-INF/pages/" /> <!-- 页面存放路径 --><property name="suffix" value=".jsp" /> <!-- 页面后缀 --></bean><!-- 开启MVC注解驱动(默认加载映射器、适配器) --><mvc:annotation-driven /></beans>2. 基本数据类型 + 字符串绑定
场景:简单表单提交,直接获取单个参数(如用户名、年龄)。
步骤 1:编写 JSP 表单
<!-- webapp/paramBinding.jsp --><%@ page contentType="text/html;charset=UTF-8" language="java" %><html><head><title>基本类型参数绑定</title></head><body><h3>基本数据类型 + 字符串绑定</h3><form action="/user/save1.do" method="post">姓名:<input type="text" name="username" placeholder="请输入用户名"/><br/>年龄:<input type="text" name="age" placeholder="请输入年龄"/><br/>邮箱:<input type="text" name="email" placeholder="请输入邮箱"/><br/><input type="submit" value="提交" /></form></body></html>步骤 2:编写控制器方法
// cn.tx.demo2.UserController@Controller@RequestMapping("/user") // 一级访问路径public class UserController {/*** 基本类型+字符串绑定:参数名与表单name一致* 注意:基本类型(如int)不能接收null值,建议用包装类(Integer)*/@RequestMapping("/save1.do")public String save1(String username, Integer age, String email) {System.out.println("用户名:" + username);System.out.println("年龄:" + age);System.out.println("邮箱:" + email);return "suc"; // 跳转至WEB-INF/pages/suc.jsp}}关键注意点
- 表单 name 必须与方法参数名完全一致(如 username 对应 String username)。
- 基本类型(int、double)无法接收 null,若表单可能漏填,建议用包装类(Integer、Double)。
- 字符串类型可接收任意文本(包括中文,需解决乱码,后续说明)。
3. 实体类型(JavaBean)绑定
场景:表单字段较多时,将参数封装到 JavaBean 对象中(如用户信息、订单信息)。
步骤 1:定义 JavaBean(含嵌套引用类型)
// 主实体类:Userpackage cn.tx.demo2;import java.io.Serializable;import java.util.List;public class User implements Serializable {private String username; // 用户名private Integer age; // 年龄private Address address; // 嵌套引用类型(地址信息)private List<Address> addressList; // 集合类型(多个地址)// 全参构造、无参构造(可选,建议保留)// Getter + Setter(必须!SpringMVC通过Setter注入值)// toString()(方便打印调试)@Overridepublic String toString() {return "User{" +"username='" + username + '\'' +", age=" + age +", address=" + address +", addressList=" + addressList +'}';}// Getter和Setter省略,IDE自动生成即可}// 嵌套实体类:Addresspackage cn.tx.demo2;import java.io.Serializable;public class Address implements Serializable {private String province; // 省份private String city; // 城市private Double money; // 金额// Getter + Setter + toString()@Overridepublic String toString() {return "Address{" +"province='" + province + '\'' +", city='" + city + '\'' +", money=" + money +'}';}}步骤 2:JSP 表单(支持嵌套和集合)
<!-- 实体类+嵌套引用+集合绑定 --><h3>实体类绑定(含嵌套和集合)</h3><form action="/user/save2.do" method="post">姓名:<input type="text" name="username" /><br/>年龄:<input type="text" name="age" /><br/><!-- 嵌套引用类型:对象.属性名 -->收货省份:<input type="text" name="address.province" /><br/>收货城市:<input type="text" name="address.city" /><br/><!-- 集合类型:list[索引].属性名 -->地址1金额:<input type="text" name="addressList[0].money" /><br/>地址2金额:<input type="text" name="addressList[1].money" /><br/><input type="submit" value="提交" /></form>步骤 3:控制器方法(直接接收 JavaBean)
/*** 实体类绑定:直接接收User对象* SpringMVC自动将表单参数注入到User的属性中(含嵌套对象和集合)*/@RequestMapping("/save2.do")public String save2(User user) {System.out.println("封装后的User对象:" + user);return "suc";}打印结果示例
封装后的User对象:User{username='张三',age=25,address=Address{province='广东省', city='深圳市'},addressList=[Address{province='null', city='null', money=100.0},Address{province='null', city='null', money=200.0}]}核心规则
- 普通属性:表单 name = JavaBean 属性名(如 username)。
- 嵌套引用:表单 name = 引用对象名。属性名(如 address.province)。
- List 集合:表单 name = 集合名 [索引]. 属性名(如 addressList[0].money)。
- JavaBean 必须提供 无参构造 和 Setter 方法(SpringMVC 通过反射注入)。
4. Map 集合绑定(拓展)
场景:不确定参数名时,用 Map 接收动态参数。
步骤 1:JSP 表单
<h3>Map集合绑定</h3><form action="/user/save3.do" method="post">键1:<input type="text" name="map['key1']" value="value1"/><br/>键2:<input type="text" name="map['key2']" value="value2"/><br/>用户姓名:<input type="text" name="username" value="李四"/><br/><input type="submit" value="提交" /></form>步骤 2:定义含 Map 的 JavaBean
public class User implements Serializable {private String username;private Map<String, String> map; // Map集合属性// Getter + Setter + toString()}步骤 3:控制器方法
@RequestMapping("/save3.do")public String save3(User user) {System.out.println("Map集合数据:" + user.getMap());System.out.println("用户名:" + user.getUsername());return "suc";}打印结果
Map集合数据:{key1=value1, key2=value2}用户名:李四三、请求参数中文乱码解决
表单提交中文时,默认会出现乱码(Tomcat 默认编码为 ISO-8859-1),SpringMVC 提供了现成的过滤器解决方案:
配置步骤(web.xml 中添加)
<!-- 中文乱码过滤器:必须放在所有过滤器之前 --><filter><filter-name>characterEncodingFilter</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>characterEncodingFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping>注意事项
- 过滤器顺序:必须放在最前面(尤其是在 Shiro、Security 等过滤器之前)。
- 仅对 POST 请求生效:GET 请求乱码需修改 Tomcat 的 server.xml(添加 URIEncoding="UTF-8")。
四、自定义类型转换器
SpringMVC 默认支持基本类型转换(如 String→int),但复杂类型(如 String→Date)需要自定义转换器。
两种实现方式
方式 1:@DateTimeFormat 注解(简单场景)
直接在 JavaBean 的 Date 属性上添加注解,指定日期格式:
public class User implements Serializable {// 其他属性...// 日期格式:yyyy-MM-dd(如2024-05-20)@DateTimeFormat(pattern = "yyyy-MM-dd")private Date birthday;// Getter + Setter}JSP 表单:
生日:<input type="text" name="birthday" placeholder="格式:2024-05-20"/><br/>方式 2:实现 Converter 接口(通用场景)
当需要全局复用转换器(如多种日期格式、自定义对象转换)时,推荐这种方式。
步骤 1:自定义转换器类
package cn.tx.demo2;import org.springframework.core.convert.converter.Converter;import java.text.ParseException;import java.text.SimpleDateFormat;import java.util.Date;/*** 自定义转换器:String → Date* 实现Converter<S, T>接口:S=源类型,T=目标类型*/public class StringToDateConverter implements Converter<String, Date> {@Overridepublic Date convert(String source) {// 1. 校验参数if (source == null || source.trim().isEmpty()) {throw new RuntimeException("日期参数不能为空!");}// 2. 定义日期格式(支持多种格式可扩展)SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");try {// 3. 转换并返回return sdf.parse(source);} catch (ParseException e) {throw new RuntimeException("日期格式错误!请输入yyyy-MM-dd格式");}}}步骤 2:注册转换器(springmvc.xml)
<!-- 1. 配置转换器工厂,注入自定义转换器 --><bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"><property name="converters"><set><!-- 注入自定义的日期转换器 --><bean class="cn.tx.demo2.StringToDateConverter" /></set></property></bean><!-- 2. 让MVC注解驱动使用自定义转换器 --><mvc:annotation-driven conversion-service="conversionService" />步骤 3:测试使用
JSP 表单与方式 1 一致,控制器直接接收 Date 类型参数:
@RequestMapping("/save4.do")public String save4(User user) {System.out.println("生日:" + user.getBirthday()); // 打印:Fri May 20 00:00:00 CST 2024return "suc";}五、使用原生 Servlet API
SpringMVC 支持在控制器方法中直接获取原生 Servlet 对象(如 HttpServletRequest、HttpServletResponse),无需手动创建。
代码示例
import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpSession;/*** 直接接收原生Servlet API:参数列表中直接声明即可*/@RequestMapping("/save5.do")public String save5(HttpServletRequest request,HttpServletResponse response,HttpSession session // 也可直接声明HttpSession) {// 1. 获取请求参数String username = request.getParameter("username");String age = request.getParameter("age");System.out.println("原生API获取参数:" + username + "," + age);// 2. 操作Sessionsession.setAttribute("user", username);String sessionUser = (String) session.getAttribute("user");System.out.println("Session中的用户:" + sessionUser);// 3. 操作响应(如设置响应头)response.setContentType("text/html;charset=UTF-8");return "suc";}支持的原生对象
- HttpServletRequest
- HttpServletResponse
- HttpSession
- java.security.Principal
- Locale(国际化相关)
- InputStream/OutputStream
- Reader/Writer
六、常见问题与避坑指南
1.参数绑定失败(报 400 错误)
- 原因:表单 name 与参数名 / JavaBean 属性名不一致;基本类型接收了 null 值。
- 解决:检查 name 属性拼写;用包装类(Integer)替代基本类型(int)。
2.中文乱码
- 原因:未配置中文过滤器;过滤器顺序错误。
- 解决:按步骤配置 CharacterEncodingFilter;确保过滤器在最前面。
3.日期转换失败
- 原因:未配置自定义转换器;日期格式与注解指定格式不一致。
- 解决:使用 @DateTimeFormat 或自定义 Converter;确保表单日期格式匹配。
4.JavaBean 属性无法注入
- 原因:未提供 Setter 方法;属性名与表单 name 不一致;无参构造缺失。
- 解决:生成完整的 Setter 方法;检查 name 属性;保留无参构造。
