一、什么是 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:开启注解扫描、配置视图解析器
<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/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://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:annotation-driven />
</beans>
2. 基本数据类型 + 字符串绑定
场景:简单表单提交,直接获取单个参数(如用户名、年龄)。
步骤 1:编写 JSP 表单
html
<!-- 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:编写控制器方法
java
// 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(含嵌套引用类型)
java
// 主实体类:User
package 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()(方便打印调试)
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", age=" + age +
", address=" + address +
", addressList=" + addressList +
'}';
}
// Getter和Setter省略,IDE自动生成即可
}
// 嵌套实体类:Address
package cn.tx.demo2;
import java.io.Serializable;
public class Address implements Serializable {
private String province; // 省份
private String city; // 城市
private Double money; // 金额
// Getter + Setter + toString()
@Override
public String toString() {
return "Address{" +
"province='" + province + '\'' +
", city='" + city + '\'' +
", money=" + money +
'}';
}
}
步骤 2:JSP 表单(支持嵌套和集合)
html
<!-- 实体类+嵌套引用+集合绑定 -->
<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)
java
/**
* 实体类绑定:直接接收User对象
* SpringMVC自动将表单参数注入到User的属性中(含嵌套对象和集合)
*/
@RequestMapping("/save2.do")
public String save2(User user) {
System.out.println("封装后的User对象:" + user);
return "suc";
}
打印结果示例
java
封装后的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 表单
html
<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
java
public class User implements Serializable {
private String username;
private Map<String, String> map; // Map集合属性
// Getter + Setter + toString()
}
步骤 3:控制器方法
java
@RequestMapping("/save3.do")
public String save3(User user) {
System.out.println("Map集合数据:" + user.getMap());
System.out.println("用户名:" + user.getUsername());
return "suc";
}
打印结果
java
Map集合数据:{key1=value1, key2=value2}
用户名:李四
三、请求参数中文乱码解决
表单提交中文时,默认会出现乱码(Tomcat 默认编码为 ISO-8859-1),SpringMVC 提供了现成的过滤器解决方案:
配置步骤(web.xml 中添加)
java
<!-- 中文乱码过滤器:必须放在所有过滤器之前 -->
<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 属性上添加注解,指定日期格式:
java
public class User implements Serializable {
// 其他属性...
// 日期格式:yyyy-MM-dd(如2024-05-20)
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date birthday;
// Getter + Setter
}
JSP 表单:
html
生日:<input type="text" name="birthday" placeholder="格式:2024-05-20"/><br/>
方式 2:实现 Converter 接口(通用场景)
当需要全局复用转换器(如多种日期格式、自定义对象转换)时,推荐这种方式。
步骤 1:自定义转换器类
java
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> {
@Override
public 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)
java
<!-- 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 类型参数:
java
@RequestMapping("/save4.do")
public String save4(User user) {
System.out.println("生日:" + user.getBirthday()); // 打印:Fri May 20 00:00:00 CST 2024
return "suc";
}
五、使用原生 Servlet API
SpringMVC 支持在控制器方法中直接获取原生 Servlet 对象(如 HttpServletRequest、HttpServletResponse),无需手动创建。
代码示例
java
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. 操作Session
session.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 属性;保留无参构造。