@RequestMapping 注解详解
一、概述
@RequestMapping 是 Spring MVC 中最核心的映射注解,用于将 HTTP 请求映射到 Controller 的处理方法上。它既可以标注在类上,也可以标注在方法上。
- 类级别:定义该 Controller 下所有方法的公共路径前缀。
- 方法级别:定义具体方法的路径和匹配条件。
两者组合起来构成完整的请求路径。
java
@RestController
@RequestMapping("/api/users")
public class UserController {
@RequestMapping("/list")
public List<User> list() {
return userService.list();
}
}
完整路径是 /api/users/list。
二、核心属性
1. value / path
指定请求路径,两者互为别名。
java
@RequestMapping("/user")
@RequestMapping(value = "/user")
@RequestMapping(path = "/user")
三种写法完全等价。
支持多个路径:
java
@RequestMapping({"/user", "/member"})
public User getUser() { }
/user 和 /member 都可以访问这个方法。
2. method
指定 HTTP 方法,不写则匹配所有方法。
java
@RequestMapping(value = "/user", method = RequestMethod.GET)
@RequestMapping(value = "/user", method = {RequestMethod.GET, RequestMethod.POST})
method 是一个 RequestMethod 枚举数组,可以指定多个。
3. params
指定请求参数的匹配条件,可以要求参数存在、不存在、等于某个值、不等于某个值。
java
// 必须携带 type 参数
@RequestMapping(value = "/list", params = "type")
// 必须携带 type=admin
@RequestMapping(value = "/list", params = "type=admin")
// 不能携带 type 参数
@RequestMapping(value = "/list", params = "!type")
// 同时满足多个条件
@RequestMapping(value = "/list", params = {"type=admin", "page"})
4. headers
指定请求头的匹配条件,用法与 params 类似。
java
@RequestMapping(value = "/api", headers = "X-API-VERSION=1")
@RequestMapping(value = "/api", headers = "!X-DEBUG")
5. consumes
指定请求的 Content-Type,限制请求体的媒体类型。
java
@RequestMapping(value = "/user", method = RequestMethod.POST,
consumes = "application/json")
表示只接受 Content-Type: application/json 的请求。如果客户端发送的是 application/xml,会返回 415(Unsupported Media Type)。
6. produces
指定响应的 Content-Type,限制返回的媒体类型。
java
@RequestMapping(value = "/user", produces = "application/json")
表示只返回 application/json 类型的响应。客户端 Accept 头不匹配时返回 406(Not Acceptable)。
三、派生注解
Spring 4.3 之后提供了一组组合注解,简化常用的 HTTP 方法映射:
| 注解 | 等价于 |
|---|---|
@GetMapping |
@RequestMapping(method = RequestMethod.GET) |
@PostMapping |
@RequestMapping(method = RequestMethod.POST) |
@PutMapping |
@RequestMapping(method = RequestMethod.PUT) |
@DeleteMapping |
@RequestMapping(method = RequestMethod.DELETE) |
@PatchMapping |
@RequestMapping(method = RequestMethod.PATCH) |
java
@GetMapping("/user/{id}")
public User getUser(@PathVariable Long id) { }
@PostMapping("/user")
public User createUser(@RequestBody User user) { }
日常开发中优先用这些派生注解,比 @RequestMapping(method = ...) 更简洁。
四、路径匹配规则
1. Ant 风格路径
| 通配符 | 含义 | 示例 |
|---|---|---|
? |
匹配任意单个字符 | /user? 匹配 /user1、/usera |
* |
匹配任意字符(不跨路径层级) | /user/* 匹配 /user/list,不匹配 /user/list/detail |
** |
匹配任意层级路径 | /user/** 匹配 /user/list、/user/list/detail |
java
@RequestMapping("/user/*")
public User getUser() { } // 匹配 /user/list、/user/1
@RequestMapping("/user/**")
public User getUser() { } // 匹配 /user/list、/user/list/detail
2. 路径变量
用 {} 定义路径变量,配合 @PathVariable 获取。
java
@GetMapping("/user/{id}")
public User getUser(@PathVariable Long id) { }
访问 /user/1 时,id 为 1。
多个路径变量:
java
@GetMapping("/user/{userId}/order/{orderId}")
public Order getOrder(@PathVariable Long userId,
@PathVariable Long orderId) { }
路径变量带正则约束:
java
@GetMapping("/user/{id:\\d+}") // id 只能是数字
public User getUser(@PathVariable Long id) { }
3. 路径匹配中的优先级
当多个映射都能匹配同一个请求时,Spring 会选择最精确的那个:
精确路径 > 路径变量 > 通配符
java
@GetMapping("/user/list") // 优先级最高
@GetMapping("/user/{id}") // 次之
@GetMapping("/user/*") // 再次
@GetMapping("/user/**") // 最低
五、类级别与方法级别的组合
java
@RestController
@RequestMapping("/api")
public class UserController {
@GetMapping("/users")
public List<User> list() { } // 完整路径:/api/users
@GetMapping("/users/{id}")
public User get(@PathVariable Long id) { } // /api/users/{id}
}
类级别的路径会作为前缀拼接到方法路径前。方法路径可以以 / 开头,也可以不写,效果一样。
六、完整示例
java
@RestController
@RequestMapping("/api/users")
public class UserController {
// GET /api/users
@GetMapping
public List<User> list() { }
// GET /api/users/1
@GetMapping("/{id}")
public User getById(@PathVariable Long id) { }
// GET /api/users/search?name=张三
@GetMapping(value = "/search", params = "name")
public List<User> search(@RequestParam String name) { }
// POST /api/users,只接受 JSON
@PostMapping(consumes = "application/json")
public User create(@RequestBody User user) { }
// PUT /api/users/1
@PutMapping("/{id}")
public User update(@PathVariable Long id, @RequestBody User user) { }
// DELETE /api/users/1
@DeleteMapping("/{id}")
public void delete(@PathVariable Long id) { }
// 只接受 X-API-VERSION=2 的请求,返回 JSON
@GetMapping(value = "/v2/list",
headers = "X-API-VERSION=2",
produces = "application/json")
public List<User> listV2() { }
}
七、底层处理机制
@RequestMapping 由 RequestMappingHandlerMapping 处理。容器启动时,它会扫描所有 @Controller 和 @RequestMapping 标注的类和方法,生成 RequestMappingInfo 对象,并注册到映射表中。
RequestMappingInfo 封装了以下条件:
- 路径模式(PatternsRequestCondition)
- HTTP 方法(RequestMethodsRequestCondition)
- 请求参数(ParamsRequestCondition)
- 请求头(HeadersRequestCondition)
- 消费媒体类型(ConsumesRequestCondition)
- 生产媒体类型(ProducesRequestCondition)
请求到达时,DispatcherServlet 调用 RequestMappingHandlerMapping.getHandler(),遍历所有 RequestMappingInfo,找到匹配的那个,返回对应的 HandlerExecutionChain。
匹配时,多个条件必须同时满足。路径、方法、参数、请求头、媒体类型都是"与"的关系。
八、常见问题
1. 路径冲突
两个方法映射了同一个路径和方法:
java
@GetMapping("/user")
public User getUser() { }
@GetMapping("/user")
public User getUser2() { }
启动报 Ambiguous mapping。需要修改其中一个的路径或 HTTP 方法。
2. 路径变量和普通路径冲突
java
@GetMapping("/user/{id}")
public User getById(@PathVariable String id) { }
@GetMapping("/user/list")
public List<User> list() { }
Spring 会优先匹配精确路径 /user/list,不会冲突。但如果 {id} 用了正则约束,比如 {id:\\d+},/user/list 不会被 {id} 匹配。
3. consumes 导致 415
java
@PostMapping(value = "/user", consumes = "application/json")
客户端发送 Content-Type: text/plain 时返回 415。检查请求头是否正确。
4. produces 导致 406
java
@GetMapping(value = "/user", produces = "application/json")
客户端 Accept: text/html 时返回 406。要么修改客户端的 Accept 头,要么去掉 produces 限制。
5. 类级别和方法级别路径拼接问题
java
@RestController
@RequestMapping("/api/")
public class UserController {
@GetMapping("/users")
public List<User> list() { } // 实际路径:/api/users
}
类级别路径末尾的 / 会被处理,不会产生 //。但建议路径不要带多余斜杠,保持规范。
九、最佳实践
- 优先使用
@GetMapping、@PostMapping等派生注解,比@RequestMapping(method = ...)更简洁。 - 类级别用
@RequestMapping定义模块前缀,方法级别用派生注解定义具体路径。 - 路径命名用名词复数,如
/users、/orders,符合 RESTful 风格。 - 需要限制媒体类型时用
consumes和produces,避免接口被错误调用。 - 路径变量用正则约束,如
{id:\\d+},避免非法参数进入方法。 - 避免两个方法映射完全相同的路径和方法,启动时会报冲突。
十、总结
| 属性 | 作用 | 常用度 |
|---|---|---|
value / path |
请求路径 | ⭐⭐⭐⭐⭐ |
method |
HTTP 方法 | ⭐⭐⭐⭐⭐ |
params |
请求参数条件 | ⭐⭐⭐ |
headers |
请求头条件 | ⭐⭐ |
consumes |
请求体媒体类型 | ⭐⭐⭐ |
produces |
响应媒体类型 | ⭐⭐⭐ |
| 派生注解 | 对应 HTTP 方法 |
|---|---|
@GetMapping |
GET |
@PostMapping |
POST |
@PutMapping |
PUT |
@DeleteMapping |
DELETE |
@PatchMapping |
PATCH |
@RequestMapping 是 Spring MVC 请求映射的核心。理解它的路径匹配规则、方法限制、参数和媒体类型条件,就能精确控制哪些请求交给哪个方法处理。日常开发中,类级别用 @RequestMapping 定义前缀,方法级别用 @GetMapping 等派生注解,是最清晰、最常用的写法。