目录
[一、使用 @RequestParam 解决URL参数不一致的问题](#一、使用 @RequestParam 解决URL参数不一致的问题)
[二、使用 @RequestHeader 获取Http请求消息头](#二、使用 @RequestHeader 获取Http请求消息头)
[1.@RequestHeader 注解介绍:](#1.@RequestHeader 注解介绍:)
一、使用 @RequestParam 解决URL参数不一致的问题
1.@RequestParam注解介绍:
在 SpringMVC 的默认机制下,控制器(Handler)方法的 形参名 必须与请求 URL 中的 参数名 完全一致,这样程序才能准确完成数据绑定。但在实际开发中,我们经常会遇到参数名不匹配的情况。例如:前端提交的 URL 是 .../test?user_name=Cyan,而你在后端定义的方法形参是 String username。此时,SpringMVC 无法自动识别,就会导致 username 接收到的值为 null,这当然不是我们希望的结果。
**@RequestParam 注解的作用就是建立起"请求参数"与"方法形参"之间的映射关系。**它主要包含三个重要属性:
1) value / name :指定请求参数的名称。通过设置此属性,我们可以强制将 URL 中的 user_name 绑定到后端的 username 形参上。
2) required :布尔值,默认为 true, 表示请求中必须包含该参数,如果没有,系统就会抛出 400 错误。如果我们手动将其设置为 false,就表示在收到的Http请求中,这个参数可有可无。
3) defaultValue:默认值,意思就是说如果请求中没有携带该参数,则使用给定的默认值,这在处理分页参数(如 pageNo)时很有用。
2.实例:
需求:现在有一个处理 "Phone" 的 PhoneHandler类,类中定义了一个 searchPhone 方法,该方法的形参名是 phoneId, 所以这个方法是用于查询指定 phoneId 的手机。但是如果浏览器访问的 URL 中提交的参数名不是 phoneId,比如 URL 中提交的参数名是 id,我们该如何处理?
PhoneHandler类代码如下:
java
package com.cyan.web.requestparam;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
/**
* @author : Cyan_RA9
* @version : 23.0
*/
@RequestMapping(value = "/phone")
@Controller
public class PhoneHandler {
@GetMapping(value = "/search")
public String searchPhone(@RequestParam(value = "id", required = false) Integer phoneId) {
/*
1) @RequestParam 注解 表示会接收提交的参数;
2) value = "id", 表示提交的参数名是"id"
3) required = false, 表示这个参数可以不出现在请求的URL中. 默认是true
*/
System.out.println("查询 phoneId = " + phoneId + " 的手机~");
return "success";
}
}
PhoneHandler 的 searchPhone方法 return 的 "success" 就是我们之前编写的 success.jsp 页面,代码也很简单,如下:
html
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>原神牛逼</title>
</head>
<body>
<h2>Operation Successful!</h2>
</body>
</html>
再来编写一个简单的 JSP 页面,requestparam.jsp 代码如下:(通过超链接的形式发出 GET 请求)
html
<%--
User : Cyan_RA9
Version : 24.0
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>原神不牛逼</title>
</head>
<body>
<h2>捕获下面这个超链接的请求参数</h2>
<a href="phone/search?id=141">我是超链接, 快点我</a>
</body>
</html>
测试效果如下 GIF图所示:

二、使用 @RequestHeader 获取Http请求消息头
1.@RequestHeader 注解介绍:
HTTP 请求不仅仅包含 URL 参数和表单数据,它的 "消息头(Request Header)"中还隐藏着丰富的元数据信息,比如 User-Agent:告诉服务器浏览器的类型及操作系统;Accept-Language:告知服务器客户端支持的语言;Cookie:携带本地存储的会话信息。
在传统的 Servlet 开发中,我们需要通过 request.getHeader("HeaderName") 手动获取这些消息头。而在 SpringMVC 中,我们可以直接使用 @RequestHeader 注解,直接在 Handler 方法的形参上声明并获取这些信息。通过 @RequestHeader 注解,我们可以非常方便地实现根据浏览器类型做一些 逻辑跳转、多语言国际化处理 或 权限校验等功能。而且,@RequestHeader 注解同样支持 required 和 defaultValue 属性,以保证程序的健壮性。
2.实例:
需求:在上面的 PhoneHandler类中 新定义一个 searchHeaders 方法,专门用来获取Http请求头。
PhoneHandler类代码如下:
java
package com.cyan.web.requestparam;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
/**
* @author : Cyan_RA9
* @version : 23.0
*/
@RequestMapping(value = "/phone")
@Controller
public class PhoneHandler {
@GetMapping(value = "/search")
public String searchPhone(@RequestParam(value = "id", required = false) Integer phoneId) {
/*
1) @RequestParam 注解 表示会接收提交的参数;
2) value = "id", 表示提交的参数名是"id"
3) required = false, 表示这个参数可以不出现在请求的URL中. 默认是true
*/
System.out.println("查询 phoneId = " + phoneId + " 的手机~");
return "success";
}
@GetMapping(value = "/searchEX")
public String searchHeaders(@RequestHeader("Host") String host,
@RequestHeader("Connection") String connection,
@RequestHeader(value = "Content-Type", required = false) String contentType) {
System.out.println("Host = " + host);
System.out.println("Connection = " + connection);
System.out.println("Content-Type = " + contentType);
return "success";
}
}
我们可以直接在浏览器地址栏,进行测试,如下图所示:

运行结果如下:

三、表单提交的数据自动封装为JavaBean
1.自动封装的原理:
在 SpringMVC 中,有一个非常强大的特性:POJO(Plain Old Java Object)自动绑定 。 也就是说,我们只需在 Handler 方法的形参上直接定义一个 JavaBean(例如 User 对象),当表单提交时,SpringMVC 底层会自动实例化这个类,并将请求参数填充进去,自动完成数据的封装。
但是 SpringMVC 是如何做到这一点的?
1) 反射机制 :SpringMVC 的参数解析器(如 ServletModelAttributeMethodProcessor)首先通过反射调用 JavaBean 的无参构造器 ,在内存中创建一个空的 POJO 实例。
2) 调用 Setter :接着,SpringMVC 会遍历请求中的所有参数,寻找与参数名同名 的属性。它的判断依据并不是成员变量名,而是 JavaBean 的 Setter 方法名 (符合 Java 规范)。比方说:请求参数名为 username,它就会寻找并调用 setUsername() 方法进行赋值。
3) 类型转换:在赋值过程中,SpringMVC 内部的 DataBinder(数据绑定器)会自动完成类型转换(如将字符串 "18" 转换为 int类型的 18)。
在复杂的业务场景下,我们的 JavaBean 可能还嵌套了另一个对象。例如 Buyer 类中有一个 Phone 属性,而 Phone 类里也有自己的字段。这时候的属性注入就是"++级联属性注入++ "。
要实现这种级联属性的自动注入,操作非常简单: 首先是封装的对象 以及 该对象的属性对应的类 都满足JavaBean 规范。然后,在表单的 name 属性中使用 "点(.)" 路径语法即可,示例:<input type="text" name="phone.name">,SpringMVC 在解析时,会先通过 Buyer 类的 getPhone() 获取 Phone对象(如果为 null 则自动创建),然后再调用 phone.setName() 完成注入。
2.实例:
需求:编写一个 Phone 类用于模拟手机类,Buyer 类用于模拟买手机的人。然后在 PhoneHandler 中新增一个 findPhoneBuyer 方法,用来打印 Buyer 类对象。我们的目标是在前端的 form 表单中提交数据,数据会被自动封装到 findPhoneBuyer 方法形参列表定义的 Buyer类对象中,然后我们直接打印对象,查看表单传入的数据是否封装成功。
首先我们来编写 JavaBean 类。up 把这两个 JavaBean 类都放到 pojo包下,如下图所示:

Phone 类代码如下:
java
package com.cyan.web.pojo;
/**
* @author : Cyan_RA9
* @version : 23.0
*/
public class Phone {
private Integer id;
private String name;
public Phone() {}
public Phone(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Phone{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
Buyer 类代码如下:(Δ注意 Buyer 类中有一个级联属性)
java
package com.cyan.web.pojo;
/**
* @author : Cyan_RA9
* @version : 23.0
*/
public class Buyer {
private Integer id;
private String name;
private Phone phone;
public Buyer() {}
public Buyer(Integer id, String name, Phone phone) {
this.id = id;
this.name = name;
this.phone = phone;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Phone getPhone() {
return phone;
}
public void setPhone(Phone phone) {
this.phone = phone;
}
@Override
public String toString() {
return "Buyer{" +
"id=" + id +
", name='" + name + '\'' +
", phone=" + phone +
'}';
}
}
PhoneHandler类代码如下:(新定义了一个 findPhoneBuyer方法)
java
package com.cyan.web.requestparam;
import com.cyan.web.pojo.Buyer;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
/**
* @author : Cyan_RA9
* @version : 23.0
*/
@RequestMapping(value = "/phone")
@Controller
public class PhoneHandler {
@GetMapping(value = "/search")
public String searchPhone(@RequestParam(value = "id", required = false) Integer phoneId) {
/*
1) @RequestParam 注解 表示会接收提交的参数;
2) value = "id", 表示提交的参数名是"id"
3) required = false, 表示这个参数可以不出现在请求的URL中. 默认是true
*/
System.out.println("查询 phoneId = " + phoneId + " 的手机~");
return "success";
}
@GetMapping(value = "/searchEX")
public String searchHeaders(@RequestHeader("Host") String host,
@RequestHeader("Connection") String connection,
@RequestHeader(value = "Content-Type", required = false) String contentType) {
System.out.println("Host = " + host);
System.out.println("Connection = " + connection);
System.out.println("Content-Type = " + contentType);
return "success";
}
/**
* 表单提交数据 -> 自动封装为POJO类
* 1) 只要在方法的形参直接声明对应的类型, SpringMVC 底层会对传入的数据进行自动封装
* 但是要求传入参数的参数名 必须和 对应对象的字段名保持一致.
* 2) 匹配失败的字段自动为 null
*/
@PostMapping(value = "/findBuyer")
public String findPhoneBuyer(Buyer buyer) {
System.out.println("买手机的人是:" + buyer);
return "success";
}
}
然后,我们就在原来的 requestparam.jsp 页面下,新增一个 POST 表单,用于提交数据,代码如下:
html
<%--
User : Cyan_RA9
Version : 24.0
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>原神不牛逼</title>
</head>
<body>
<h2>捕获下面这个超链接的请求参数</h2>
<a href="phone/search?id=141">我是超链接, 快点我</a>
<hr>
<form action="phone/findBuyer" method="post">
<table style="border: 2px cornflowerblue solid">
<tr>
<td>买主id:</td>
<td><input type="text" name="id"></td>
</tr>
<tr>
<td>买主name:</td>
<td><input type="text" name="name"></td>
</tr>
<tr>
<td>手机id:</td>
<td><input type="text" name="phone.id"></td>
</tr>
<tr>
<td>手机name:</td>
<td><input type="text" name="phone.name"></td>
</tr>
<tr>
<td><input type="submit" value="SUBMIT"/></td>
<td><input type="reset" value="RESET"/></td>
</tr>
</table>
</form>
</body>
</html>
然后我们提交表单,如下图所示:

后端运行结果如下:

Δ总结
- 🆗,以上就是本篇博文的全部内容了,感谢阅读!
- 总的来说,整篇博文的三部分内容 本质上都在做同一件事:将非结构化的 HTTP 请求数据(字符串、键值对、消息头)"映射"到结构化的 Java 世界(形参、对象、POJO)中。 比如,@RequestParam是将 URL/Body 参数 映射到 简单形参;@RequestHeader是将 HTTP 报文头 映射到 方法变量;而 JavaBean 封装:是将 整个表单 映射到 内存对象。
- 这里顺便再提一嘴,如果想用原生的 servlet api,需要先引入tomcat/lib 下的 servlet-api.jar。你想用吗?
- 文章整体上都是"理论"+"实战" 的结构,应该还是比较清晰的,大家可以自己动手写写、敲一敲。