1. 概述
在 Spring MVC 开发中,@RequestParam
和 @PathVariable
是两个高频使用的注解,用于从 HTTP 请求中提取参数。虽然它们都能获取请求中的数据,但适用场景和行为有本质区别。
本文将结合实际示例,帮你彻底理清两者的差异,避免日常开发中“踩坑”。
2. 查询参数 vs 路径变量
✅ 核心区别一句话总结:@RequestParam
用于获取 URL 中的查询参数(query parameter),而 @PathVariable
用于提取 URI 模板中的路径变量(path variable)。
使用 @PathVariable 提取路径变量
假设我们有一个根据 ID 获取资源的接口:
@GetMapping("/foos/{id}")
@ResponseBody
public String getFooById(@PathVariable String id) {
return "ID: " + id;
}
此时,URL 路径中的 {id}
是一个占位符,Spring 会自动将其绑定到 @PathVariable
注解的参数上。
访问如下地址:
http://localhost:8080/spring-mvc-basics/foos/abc
输出结果为:
ID: abc
使用 @RequestParam 获取查询参数
同样的功能也可以通过查询参数实现:
@GetMapping("/foos")
@ResponseBody
public String getFooByIdUsingQueryParam(@RequestParam String id) {
return "ID: " + id;
}
对应的请求 URL 变为:
http://localhost:8080/spring-mvc-basics/foos?id=abc
输出结果相同:
ID: abc
📌 总结对比:
特性 | @PathVariable | @RequestParam |
---|---|---|
来源 | URI 路径模板 | 查询字符串(query string) |
示例 URL | /foos/abc |
/foos?id=abc |
是否必须编码 | 否(原始值) | 是(自动解码) |
3. 编码处理:原始值 vs 解码值
⚠️ 这是一个容易被忽视但非常关键的差异!
由于 @PathVariable
从 URI 路径中提取值,该值不会被自动 URL 解码。
而 @RequestParam
获取的是查询参数,Spring 会自动对其进行 URL 解码。
示例说明
假设我们要传递的值包含 +
符号(代表空格)。
使用 @PathVariable
http://localhost:8080/spring-mvc-basics/foos/ab+c
对应方法:
@GetMapping("/foos/{id}")
@ResponseBody
public String getFooById(@PathVariable String id) {
return "ID: " + id;
}
输出结果为:
ID: ab+c
👉 +
没有被解码,保持原样。
使用 @RequestParam
http://localhost:8080/spring-mvc-basics/foos?id=ab+c
对应方法:
@GetMapping("/foos")
@ResponseBody
public String getFooByIdUsingQueryParam(@RequestParam String id) {
return "ID: " + id;
}
输出结果为:
ID: ab c
👉 +
被自动解码为空格。
💡 原因:在 URL 编码中,+
表示空格,仅适用于 query 部分。路径部分的 +
不会被自动转换。
所以如果你在路径中需要传递特殊字符,建议手动编码(如用 %20
替代空格),否则可能拿到非预期的值。
4. 可选参数的处理
✅ 两者都支持可选参数,但实现方式和注意事项不同。
@RequestParam 的可选性
这是常见操作,通过 required = false
设置:
@GetMapping("/foos")
@ResponseBody
public String getFooWithOptionalParam(@RequestParam(required = false) String id) {
return "ID: " + (id != null ? id : "default");
}
访问 /foos
时,id
为 null
,可做默认处理。
@PathVariable 的可选性
⚠️ 注意:@PathVariable
的可选支持是从 Spring 4.3.3 开始才有的。
使用方式:
@GetMapping({"/myfoos/optional", "/myfoos/optional/{id}"})
@ResponseBody
public String getFooByOptionalId(@PathVariable(required = false) String id) {
return "ID: " + id;
}
支持两种访问方式:
http://localhost:8080/spring-mvc-basics/myfoos/optional/abc
输出:
ID: abc
http://localhost:8080/spring-mvc-basics/myfoos/optional
输出:
ID: null
❌ 踩坑提醒:
当使用可选 @PathVariable
时,必须提供多个 @GetMapping
路径(如上面的数组形式),否则无法匹配无参数的路径。
而且要小心路径冲突,比如:
@GetMapping("/users/{id}")
@GetMapping("/users/profile")
如果 {id}
是可选的,这两个路径可能产生歧义,导致请求匹配错乱。
5. 总结
对比项 | @RequestParam | @PathVariable |
---|---|---|
参数来源 | 查询字符串(?key=value) | URI 路径模板(/path/{var}) |
是否自动解码 | ✅ 是 | ❌ 否(保留原始值) |
是否可选 | ✅ 支持 required = false |
✅ Spring 4.3.3+ 支持 |
RESTful 风格推荐 | ❌ 一般不用于核心资源标识 | ✅ 推荐用于资源定位 |
典型用途 | 过滤、分页、搜索参数 | 资源 ID、层级路径 |
📌 简单粗暴记忆法:
- 要找的东西在
?
后面 →@RequestParam
- 要找的东西在
/
路径里 →@PathVariable
项目源码已整理至 GitHub:https://github.com/example/spring-mvc-basics(mock 地址)
掌握这两个注解的区别,能让你在设计 REST 接口时更清晰、更规范,少走弯路。