彻底搞懂 @GetMapping
、@PostMapping
、@PutMapping
、@DeleteMapping
等注解中路径参数的含义,以及它们和类上 @RequestMapping
的配合方式。
✅ 一、Mapping 注解和访问路径的关系
在 Spring Boot 的控制器中:
- 类上的
@RequestMapping
定义了该类中所有接口的 公共前缀。 - 方法上的
@GetMapping
、@PostMapping
等 定义了该方法的 子路径。 - 最终接口访问地址 = 类上路径 + 方法上路径。
✅ 你的示例
@RestController
@RequestMapping("/admin/order") // 类级别公共前缀
public class OrderController {
@GetMapping("/add") // 方法级别路径
public String addOrder() {
return "add order";
}
}
🔹 这里最终的访问路径是:
/admin/order/add
你的理解是完全正确的:类上的 /admin/order
和方法上的 /add
组成完整路径。
✅ 二、Mapping 注解参数详解
以 @GetMapping
为例(PostMapping
、PutMapping
、DeleteMapping
都类似),它有几个常用参数:
1️⃣ value
(或 path
)
- 表示当前方法相对于类上
@RequestMapping
的子路径。 - 可以是字符串或字符串数组。
value
和path
是同义别名,任选其一即可。
例:
@GetMapping("/list")
@GetMapping(path="/list")
以上两种写法完全等价。
2️⃣ params
- 指定 必须带上哪些请求参数 才能匹配此方法。
- 常用于区分同一路径下根据参数不同选择不同方法。
例:
@GetMapping(value="/search", params="type=quick")
public String quickSearch() { ... }
@GetMapping(value="/search", params="type=advanced")
public String advancedSearch() { ... }
🔹 /search?type=quick
会匹配到 quickSearch
🔹 /search?type=advanced
会匹配到 advancedSearch
同一路径根据请求参数不同来走不同处理逻辑
✅ 背景
在正常情况下,如果你有两个方法,它们的路径和请求方法都一样,比如都是:
GET /search
那么 Spring 会认为这是“路由冲突”:它不知道到底该走哪个方法,会抛出类似
Ambiguous mapping. Cannot map 'xxxController' method
的异常。
✅ 但是通过 params 区分
你可以在 @GetMapping
的 params
参数中 指定请求必须携带哪些参数才能匹配到这个方法,从而在同一路径下用不同的参数值区分不同方法。
✅ 你的例子
@RestController
@RequestMapping("/api")
public class SearchController {
@GetMapping(value="/search", params="type=quick")
public String quickSearch() {
return "Quick search result";
}
@GetMapping(value="/search", params="type=advanced")
public String advancedSearch() {
return "Advanced search result";
}
}
🔹 这里有两种情况:
✅ 情况 1:/api/search?type=quick
-
发送 GET 请求:
GET /api/search?type=quick
-
Spring 会检查:
- URL 匹配
/search
; - params 中有
type=quick
;
- URL 匹配
-
所以只会匹配到:
public String quickSearch()
-
响应是 "Quick search result"。
✅ 情况 2:/api/search?type=advanced
-
发送 GET 请求:
GET /api/search?type=advanced
-
URL 依然是
/search
,但参数变成了type=advanced
; -
所以只会匹配到:
public String advancedSearch()
-
响应是 "Advanced search result"。
✅ 这个 params
是什么意思?
在 @GetMapping
中:
@GetMapping(value="/search", params="type=quick")
这里的 params="type=quick"
表示:
- 这个方法只会匹配请求中带有参数
type
,并且其值必须等于quick
的请求。
✅ 使用场景
为什么要这样做?
🔹 如果你要做 同一路径,但不同搜索类型 的接口,比如搜索可以按:
type=quick
→ 快速搜索type=advanced
→ 高级搜索
而你又想让它们都用 /search
这个统一 URL(不想搞 /quickSearch
、/advancedSearch
两个接口),这时就非常适合用 params
。
✅ 注意
-
如果没有带指定参数,或者参数值不符合任何一个方法定义的条件,Spring 会返回 404。
-
你可以在
params
中同时指定多个参数,比如:@GetMapping(value="/search", params={"type=quick", "sort=asc"})
表示请求中必须同时满足
type=quick
且sort=asc
。
✅ 小结
✔️ 通过 params
你可以:
- 在同一个路径下,用不同的参数组合区分不同的接口处理方法;
- 避免在 URL 路径上区分不同功能,让 API 更加 RESTful。
✅ 一句话总结:
params="type=quick"
表示只有当 GET 请求/search
携带参数type=quick
时,Spring 才会将请求路由到对应方法。
3️⃣ headers
- 指定必须带上哪些请求头才能匹配此方法。
- 用得不多,适合对 header 严格要求的接口。
例:
@GetMapping(value="/secure", headers="X-API-KEY=mykey")
public String secureApi() { ... }
4️⃣ produces
- 指定返回的内容类型(响应类型)必须满足客户端的
Accept
头,否则不匹配。 - 适合一个接口同时支持多种返回格式。
例:
@GetMapping(value="/data", produces="application/json")
public Data getJsonData() { ... }
@GetMapping(value="/data", produces="application/xml")
public Data getXmlData() { ... }
5️⃣ consumes
- 指定接口能接收的请求体内容类型(Content-Type),常用于 POST/PUT。
例:
@PostMapping(value="/upload", consumes="application/json")
public String uploadJson(@RequestBody MyData data) { ... }
✅ 三、几个 Mapping 注解的区别
-
@RequestMapping
- 通用注解,可通过
method= RequestMethod.GET/POST/...
指定请求方法。 - 可以同时用于类或方法上。
- 更灵活,但更繁琐。
- 通用注解,可通过
-
@GetMapping
- 相当于
@RequestMapping(method=RequestMethod.GET)
的简化版本。 - 只处理 GET 请求,其他
@XxxMapping
类似。
- 相当于
-
@PostMapping
- 简化处理 POST 请求。
-
@PutMapping
- 简化处理 PUT 请求。
-
@DeleteMapping
- 简化处理 DELETE 请求。
✅ 例子:全部组合演示
@RestController
@RequestMapping("/api/v1/users") // 公共前缀:/api/v1/users
public class UserController {
@GetMapping("/{id}") // GET /api/v1/users/123
public String getUser(@PathVariable Long id) { ... }
@PostMapping // POST /api/v1/users
public String createUser() { ... }
@PutMapping("/{id}") // PUT /api/v1/users/123
public String updateUser(@PathVariable Long id) { ... }
@DeleteMapping("/{id}") // DELETE /api/v1/users/123
public String deleteUser(@PathVariable Long id) { ... }
}
✅ 四、几点易错点
✅ 方法路径前不要随意加或漏掉斜杠:
/add
和add
效果相同,但最好统一加/
。
✅ 类上 @RequestMapping
的路径不要加尾部 /
:
- 推荐
/admin/order
而不是/admin/order/
,避免和方法路径拼接时产生双斜杠。
✅ 不要把方法上路径写成绝对路径(以 /
开头和不以 /
开头都可以,但不要搞混):
/add
和add
都能正常拼接,但统一风格更好。
🔹 在 REST 风格中, “动作”通过请求方法来区分,而不是靠路径来区分。
✅ 关键点:URL + HTTP 方法共同决定唯一接口
比如:
PUT /api/v1/users/123
→ 表示对 id=123 的用户进行更新。DELETE /api/v1/users/123
→ 表示对 id=123 的用户进行删除。
虽然路径完全一致(/api/v1/users/123
),但是:
- 客户端 在发起请求时,指定了不同的 HTTP 方法(PUT 或 DELETE);
- Spring Boot 会根据方法注解
@PutMapping
/@DeleteMapping
中内置的请求方法去匹配不同的处理函数; - 所以对于同一路径,Spring 会根据请求的方法(Method)路由到正确的处理器。
✅ 在 Java Spring 中是怎么匹配的?
Spring 底层机制:
-
@PutMapping("/{id}")
本质等价于:@RequestMapping(value="/{id}", method=RequestMethod.PUT)
-
@DeleteMapping("/{id}")
等价于:@RequestMapping(value="/{id}", method=RequestMethod.DELETE)
-
Spring DispatcherServlet 根据:
- 请求的 URL;
- 请求的 HTTP Method;
这两个条件匹配 Controller 方法。
✅ 调用示例
你通过不同方式发送不同方法的请求:
🔹 修改用户(PUT):
curl -X PUT http://localhost:8080/api/v1/users/123 \
-H "Content-Type: application/json" \
-d '{"name":"newname"}'
🔹 删除用户(DELETE):
curl -X DELETE http://localhost:8080/api/v1/users/123
- 两次请求的 URL 完全一样;
- 但第一次是
PUT
方法 → 匹配@PutMapping
; - 第二次是
DELETE
方法 → 匹配@DeleteMapping
。
✅ 这就是 RESTful API 的核心思想:
-
URL 表示 资源的位置;
-
HTTP 方法表示 对资源的动作:
GET
→ 查询POST
→ 新增PUT
→ 更新PATCH
→ 部分更新DELETE
→ 删除