Spring MVC 请求参数绑定与常用注解2

第一部分:请求参数绑定

1.1 绑定机制与前提条件

⭐ 老师强调 :Spring MVC 底层依然基于 Servlet 的 request.getParameter(),但通过方法参数列表直接声明,框架会自动完成绑定赋值。

核心要求 :表单提交的 name 属性必须与控制器方法形参名/实体类属性名完全一致(区分大小写)。

支持的数据类型:

  1. 基本数据类型和字符串类型
  2. 实体类型(JavaBean)
  3. 集合数据类型(List、Map等)
  4. 复杂嵌套类型(实体套实体、实体套集合等)

1.2 六大绑定形式

1. 基本数据类型与字符串(同名接收)

复制代码
@RequestMapping("/save1.do")
public String save(String username, Integer age) {
    // 前端 form 的 name="username" 和 name="age" 自动绑定到这里
    return "suc";
}

2. 实体类(JavaBean)接收

⭐ 老师强调 :字段多(如10个)时挨个写参数太麻烦。用实体类接收,只要 name 和实体类属性名一致,自动映射。必须有无参构造、有参构造、get/set、toString。

复制代码
public class User implements Serializable {
    private String username;
    private Integer age;
    // get/set...
}

@RequestMapping("/save2.do")
public String save2(User user) {
    System.out.println(user);
    return "suc";
}

3. 实体类嵌套实体类(点语法接收)

前端表单必须写成 对象.属性 的形式:

复制代码
金额:<input type="text" name="address.money" />

@RequestMapping("/save3.do")
public String save3(User user) {
    System.out.println(user);
    return "suc";
}

4. 实体类嵌套集合(索引接收)

前端表单必须写成 集合[索引].属性 的形式:

复制代码
集合1:<input type="text" name="list[0].money" />
集合2:<input type="text" name="list[1].money" />

@RequestMapping("/save4.do")
public String save4(User user) {
    System.out.println(user);
    return "suc";
}

5. 参数名不一致(使用 @RequestParam 绑定)

⭐ 老师强调 :当方法形参名与前端 name 不一致时,用 @RequestParam 绑定。

复制代码
@RequestMapping("/save1.do")
public String save(@RequestParam(value = "username", required = false, defaultValue = "abc") String name) {
    // required=true 默认,不传报 400;defaultValue 不传时给默认值
    return "suc";
}

6. 使用原生 Servlet API

⭐ 老师强调:框架虽然做了简化,但依然支持原生 API。只需要在方法参数中定义,框架会自动注入。

复制代码
@RequestMapping("/save6.do")
public String save6(HttpServletRequest request, HttpServletResponse response, HttpSession session) {
    System.out.println(request.getParameter("username"));
    return "suc";
}

第二部分:中文乱码与日期格式转换

2.1 乱码的根源

⭐ 老师强调:电脑底层只认 0 和 1。字符要进电脑,先按某种编码(如 UTF-8)转成字节流,再转二进制。若用 UTF-8 编码,却用 GBK 解码,就会乱码(就像 A 密码本加密,B 密码本解密)。

2.2 解决乱码的三种方式

⭐ 老师强调:第一种侵入代码、局部生效;第二种更灵活但麻烦;第三种过滤器才是最佳实践。

方式一:手动设置请求编码(局部) 在 Servlet 或 Controller 方法第一行设置:

复制代码
request.setCharacterEncoding("UTF-8");

方式二:String 重构转码(针对单一参数)

复制代码
String name = new String(request.getParameter("username").getBytes("ISO-8859-1"), "UTF-8");

方式三:配置 Spring 提供的过滤器(最佳实践) 在 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>
</filter>
<filter-mapping>
    <filter-name>characterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

⭐ 老师强调 :如果配置了过滤器还是乱码,检查是否遗漏了 <init-param> 指定编码,或者 IDEA/Tomcat 自身的编码配置不对。

2.3 日期格式转换

⭐ 老师强调 :前端表单传日期默认格式是 yyyy/MM/dd(斜杠)。如果用横杠 yyyy-MM-dd,框架会默认转换失败报错。有两种解决方案:

方式一:@DateTimeFormat 注解(推荐,简单)

直接在实体类的日期属性上加注解:

复制代码
import org.springframework.format.annotation.DateTimeFormat;

public class User implements Serializable {
    // 规定前端传这个格式的日期
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date birthday;
}

⭐ 老师强调 :加了注解后,前端必须按注解规定的格式传(如 2026-10-18),否则报 400 错误。若前端传斜杠,需把 pattern 改为 yyyy/MM/dd。注解只约束前端传入格式,不影响后台控制台打印格式。

方式二:自定义类型转换器(适合复杂格式/额外处理)

  1. 编写转换器类,实现 Converter<S, T> 接口:

    public class StringToDate implements Converter<String, Date> {
    @Override
    public Date convert(String s) {
    if(s == null) throw new RuntimeException("请输入内容");
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    try {
    return sdf.parse(s);
    } catch (ParseException e) {
    throw new RuntimeException(e);
    }
    }
    }

  2. 在 springmvc.xml 中注册转换器:

    <mvc:annotation-driven conversion-service="conversionService" />

⭐ 老师强调 :自定义转换器后,框架遇到该类转换不走默认模式,完全接管。它相比注解的优势是能对传来的字符串做其他额外处理(如复杂格式、中文处理)。

第三部分:常用注解详解

3.1 @RequestParam

作用:把请求中指定名称的参数赋值给控制器方法的形参(解决名不一致问题)。

属性:

  • value:请求参数的名字。
  • required:是否必须(默认 true,不传报错 400)。
  • defaultValue:默认值。

代码示例:

复制代码
// 前端请求:/user/query?name=张三&age=20
@RequestMapping("/query")
public String query(
        @RequestParam(value = "name", required = true) String username,
        @RequestParam(value = "age", required = false, defaultValue = "18") Integer userAge) {
    System.out.println("用户名:" + username);   // 张三
    System.out.println("年龄:" + userAge);     // 20
    return "suc";
}

// 前端请求:/user/query?age=25  (没有传 name)
// 因为 required=true,直接报 400 错误:Required String parameter 'name' is not present

// 前端请求:/user/query?name=李四  (没有传 age)
// 因为 required=false 且有 defaultValue,userAge = 18

老师强调 :当注解只有一个 value 属性时可省略不写;设了多个属性必须写明。传空串或 null 都会触发 defaultValue。


3.2 @RequestBody

作用 :获取请求体内容(注意:GET 请求没有请求体,不能用)。

属性 :required(默认 true)。

注意 :常用于接收前端异步请求(如 Ajax)传来的 JSON 数据。可以直接用 String 接收整体请求体,也可以接收并封装为对象。

代码示例一:String 接收请求体

复制代码
// 前端 Ajax 发送 POST 请求,Content-Type: application/json
// 请求体内容:{"username":"张三","age":20,"email":"zhangsan@qq.com"}
@RequestMapping("/save")
public String save(@RequestBody String body) {
    System.out.println(body);
    // 输出:{"username":"张三","age":20,"email":"zhangsan@qq.com"}
    return "suc";
}

代码示例二:封装为对象接收(需要 Jackson 依赖)

复制代码
// 前端 Ajax 发送 POST 请求,Content-Type: application/json
// 请求体内容:{"username":"李四","age":25}
@RequestMapping("/saveUser")
public String saveUser(@RequestBody User user) {
    System.out.println(user);
    // 输出:User{username='李四', age=25}
    return "suc";
}

// 错误示范:GET 请求不能用 @RequestBody
@RequestMapping(value = "/query", method = RequestMethod.GET)
public String query(@RequestBody User user) {
    // 报错:Required request body is missing
    return "suc";
}

老师强调 :@RequestBody 只能用于 POST、PUT 等有请求体的请求方式,GET 请求没有请求体,用了会报错。


3.3 @PathVariable

作用 :绑定 URL 中的占位符 。用于支持 RESTful 风格。

属性 :value(URL 中占位符的名字)。

RESTful 风格示例:

复制代码
// 前端访问:GET /emp/1001
@RequestMapping(path = "/emp/{id}", method = RequestMethod.GET)
public String findById(@PathVariable(value = "id") Integer id) {
    System.out.println("通过id查询员工..." + id);
    // 输出:通过id查询员工...1001
    return "suc";
}

// 前端访问:DELETE /emp/1002
@RequestMapping(path = "/emp/{id}", method = RequestMethod.DELETE)
public String deleteById(@PathVariable("id") Integer id) {
    System.out.println("删除员工..." + id);
    // 输出:删除员工...1002
    return "suc";
}

// 前端访问:PUT /emp/1003  (路径一样,method 不同走不同方法)
@RequestMapping(path = "/emp/{id}", method = RequestMethod.PUT)
public String updateById(@PathVariable("id") Integer id) {
    System.out.println("更新员工..." + id);
    // 输出:更新员工...1003
    return "suc";
}

老师强调 :RESTful 风格(请求路径一样,根据 method 不同去执行不同后台方法)结构清晰,是行业标准。此时 web.xml 前端控制器的 url-pattern 要改为 /,否则 {id} 会被当作扩展名截断。访问 delete/5 时,框架会把 5 解析成 id=5 并自动绑定。


3.4 @RequestHeader 和 @CookieValue

  • @RequestHeader :获取请求头的值(如 Accept、User-Agent)。
  • @CookieValue :获取指定 Cookie 的值(如 JSESSIONID)。

代码示例:

复制代码
// 前端任意请求(浏览器自动带上请求头和 Cookie)
@RequestMapping("/header")
public String getHeader(
        @RequestHeader("Accept") String accept,
        @RequestHeader("User-Agent") String userAgent,
        @CookieValue("JSESSIONID") String sessionId) {
    System.out.println("Accept:" + accept);
    // 输出:Accept:text/html,application/xhtml+xml,...
    System.out.println("User-Agent:" + userAgent);
    // 输出:User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64)...
    System.out.println("JSESSIONID:" + sessionId);
    // 输出:JSESSIONID:A1B2C3D4E5F6...
    return "suc";
}

老师强调 :以前用原生 Servlet 获取请求头需要遍历枚举(判断 hasMoreElements(),强转 String),现在用注解直接 K-V 匹配,非常方便。


3.5 综合对比示例

注解 数据来源 适用场景 代码示意
@RequestParam URL 查询参数 ?key=value 普通表单/GET 请求 /query?name=张三
@RequestBody HTTP 请求体(JSON) Ajax 异步 POST {"name":"张三"}
@PathVariable URL 路径中的占位符 RESTful 风格 /emp/1001
@RequestHeader HTTP 请求头 获取浏览器/客户端信息 Accept: text/html
@CookieValue Cookie 获取会话 ID 等 JSESSIONID=xxx

老师总结 :日常开发中 @RequestParam、@RequestBody、@PathVariable 使用频率最高,建议结合 Ajax 异步请求和 RESTful 接口一起练习,理解各自的数据来源差异。

相关推荐
七夜zippoe1 小时前
Spring Boot 接通义千问:从拿 Key 到第一个对话接口跑通(保姆级)
java·spring boot·后端
SimonKing1 小时前
别再花钱调 OCR 接口了!这个国产开源工具,免费还能离线跑
java·后端·程序员
Nebula_g1 小时前
SpringBoot企业级实战项目—云禾外卖—SpringBoot常用注解解析
java·网络·数据库·spring boot·后端·spring·javase
const_qiu1 小时前
第三章 07:3.9 能力收束与统一入口
java·人工智能
const_qiu1 小时前
第三章 05:3.7 结构化输出与程序化消费
java·人工智能
企业数字化笔记2 小时前
租户停用后数据和任务怎么处理?冻结、恢复与保留策略的实现
android·java·spring boot
CarIise2 小时前
Spring MVC 请求参数绑定与常用注解
java·spring·mvc
一条泥憨鱼2 小时前
AI Agent 安全实践:从 MCP 工具调用到 Java 权限校验
java·人工智能·安全
Nebula_g2 小时前
云禾外卖补充—项目整体后端运行逻辑-对流程的更深层次理解
java·开发语言·网络·数据库·状态模式·javase