1. 概述

本文聚焦 Spring MVC 的核心注解之一:**@RequestMapping**。

简单说,这个注解用于将 Web 请求映射到 Spring 控制器方法。

2. @RequestMapping 基础用法

从简单示例开始:用基础条件将 HTTP 请求映射到方法。假设 Spring 默认在根上下文路径("/")提供服务,本文所有 curl 命令都基于此。

2.1. 按路径映射

@RequestMapping(value = "/ex/foos", method = RequestMethod.GET)
@ResponseBody
public String getFoosBySimplePath() {
    return "Get some Foos";
}

用 curl 测试:

curl -i http://localhost:8080/ex/foos

2.2. 按 HTTP 方法映射

HTTP 方法参数没有默认值。若不指定,会匹配所有 HTTP 请求。

下面示例映射到 POST 请求:

@RequestMapping(value = "/ex/foos", method = POST)
@ResponseBody
public String postFoos() {
    return "Post some Foos";
}

用 curl 测试 POST:

curl -i -X POST http://localhost:8080/ex/foos

3. @RequestMapping 与 HTTP 请求头

3.1. 使用 headers 属性

可通过指定请求头缩小映射范围:

@RequestMapping(value = "/ex/foos", headers = "key=val", method = GET)
@ResponseBody
public String getFoosWithHeader() {
    return "Get some Foos with Header";
}

用 curl 添加请求头测试:

curl -i -H "key:val" http://localhost:8080/ex/foos

支持多请求头:

@RequestMapping(
  value = "/ex/foos", 
  headers = { "key1=val1", "key2=val2" }, 
  method = GET)
@ResponseBody
public String getFoosWithHeaders() {
    return "Get some Foos with Header";
}

测试命令:

curl -i -H "key1:val1" -H "key2:val2" http://localhost:8080/ex/foos

⚠️ 注意:curl 中用冒号分隔键值(符合 HTTP 规范),Spring 中用等号。

3.2. consumesproduces 属性

映射控制器方法产生的媒体类型需特别注意。

可通过 headers 属性映射 Accept 请求头:

@RequestMapping(
  value = "/ex/foos", 
  method = GET, 
  headers = "Accept=application/json")
@ResponseBody
public String getFoosAsJsonFromBrowser() {
    return "Get some Foos with Header Old";
}

匹配规则灵活(使用包含而非等于),以下请求仍会匹配:

curl -H "Accept:application/json,text/html" http://localhost:8080/ex/foos

Spring 3.1+ 新增 producesconsumes 属性:

@RequestMapping(
  value = "/ex/foos", 
  method = RequestMethod.GET, 
  produces = "application/json"
)
@ResponseBody
public String getFoosAsJsonFromREST() {
    return "Get some Foos with Header New";
}

测试方式相同:

curl -H "Accept:application/json" http://localhost:8080/ex/foos

produces 支持多值:

@RequestMapping(
  value = "/ex/foos", 
  method = GET,
  produces = { "application/json", "application/xml" }
)

踩坑:新旧 Accept 映射方式本质相同,不能同时使用,否则会报错:

Caused by: java.lang.IllegalStateException: Ambiguous mapping found. 
Cannot map 'fooController' bean method ...

注意producesconsumes 与其他注解行为不同——方法级注解会覆盖类级注解而非补充。

4. 带路径变量的 @RequestMapping

URI 部分可通过 @PathVariable 绑定到变量。

4.1. 单个路径变量

简单示例:

@RequestMapping(value = "/ex/foos/{id}", method = GET)
@ResponseBody
public String getFoosBySimplePathWithPathVariable(
  @PathVariable("id") long id) {
    return "Get a specific Foo with id=" + id;
}

curl 测试:

curl http://localhost:8080/ex/foos/1

若方法参数名与路径变量名一致,可简化:

@RequestMapping(value = "/ex/foos/{id}", method = GET)
@ResponseBody
public String getFoosBySimplePathWithPathVariable(
  @PathVariable String id) {
    return "Get a specific Foo with id=" + id;
}

@PathVariable 支持自动类型转换,可直接声明为:

@PathVariable long id

4.2. 多个路径变量

复杂 URI 需映射多个值:

@RequestMapping(value = "/ex/foos/{fooid}/bar/{barid}", method = GET)
@ResponseBody
public String getFoosBySimplePathWithPathVariables
  (@PathVariable long fooid, @PathVariable long barid) {
    return "Get a specific Bar with id=" + barid + 
      " from a Foo with id=" + fooid;
}

curl 测试:

curl http://localhost:8080/ex/foos/1/bar/2

4.3. 正则表达式路径变量

可用正则限制变量格式,例如只接受数字:

@RequestMapping(value = "/ex/bars/{numericId:[\\d]+}", method = GET)
@ResponseBody
public String getBarsBySimplePathWithPathVariable(
  @PathVariable long numericId) {
    return "Get a specific Bar with id=" + numericId;
}

✅ 匹配:

http://localhost:8080/ex/bars/1

❌ 不匹配:

http://localhost:8080/ex/bars/abc

5. 带请求参数的 @RequestMapping

通过 @RequestParam 轻松映射 URL 参数。

映射请求:

http://localhost:8080/ex/bars?id=100
@RequestMapping(value = "/ex/bars", method = GET)
@ResponseBody
public String getBarBySimplePathWithRequestParam(
  @RequestParam("id") long id) {
    return "Get a specific Bar with id=" + id;
}

curl 测试(带参数):

curl -i http://localhost:8080/ex/bars --get -d id=100

高级场景下,@RequestMapping 可显式定义参数:

@RequestMapping(value = "/ex/bars", params = "id", method = GET)
@ResponseBody
public String getBarBySimplePathWithExplicitRequestParam(
  @RequestParam("id") long id) {
    return "Get a specific Bar with id=" + id;
}

支持多参数(不必全部使用):

@RequestMapping(
  value = "/ex/bars", 
  params = { "id", "second" }, 
  method = GET)
@ResponseBody
public String getBarBySimplePathWithExplicitRequestParams(
  @RequestParam("id") long id) {
    return "Narrow Get a specific Bar with id=" + id;
}

以下请求会匹配最精确的映射(同时包含 idsecond):

http://localhost:8080/ex/bars?id=100&second=something

6. @RequestMapping 特殊场景

6.1. 多路径映射到同一方法

虽单路径映射是最佳实践,但有时需多路径映射到同一方法:

@RequestMapping(
  value = { "/ex/advanced/bars", "/ex/advanced/foos" }, 
  method = GET)
@ResponseBody
public String getFoosOrBarsByPath() {
    return "Advanced - Get some Foos or Bars";
}

两个 curl 命令都会命中:

curl -i http://localhost:8080/ex/advanced/foos
curl -i http://localhost:8080/ex/advanced/bars

6.2. 多 HTTP 方法映射到同一方法

不同 HTTP 动词可映射到同一方法:

@RequestMapping(
  value = "/ex/foos/multiple", 
  method = { RequestMethod.PUT, RequestMethod.POST }
)
@ResponseBody
public String putAndPostFoos() {
    return "Advanced - PUT and POST within single method";
}

curl 测试:

curl -i -X POST http://localhost:8080/ex/foos/multiple
curl -i -X PUT http://localhost:8080/ex/foos/multiple

6.3. 全局回退映射

为特定 HTTP 方法(如 GET)实现回退:

@RequestMapping(value = "*", method = RequestMethod.GET)
@ResponseBody
public String getFallback() {
    return "Fallback for GET Requests";
}

或所有请求:

@RequestMapping(
  value = "*", 
  method = { RequestMethod.GET, RequestMethod.POST ... })
@ResponseBody
public String allFallback() {
    return "Fallback for All Requests";
}

6.4. 映射冲突错误

当 Spring 发现多个相同映射时抛出此错误。相同映射指:HTTP 方法、URL、参数、请求头、媒体类型均相同。

❌ 冲突示例:

@GetMapping(value = "foos/duplicate" )
public String duplicate() {
    return "Duplicate";
}

@GetMapping(value = "foos/duplicate" )
public String duplicateEx() {
    return "Duplicate";
}

错误信息:

Caused by: java.lang.IllegalStateException: Ambiguous mapping.
  Cannot map 'fooMappingExamplesController' method ...

解决方案:通过不同媒体类型区分:

@GetMapping(value = "foos/duplicate", produces = MediaType.APPLICATION_XML_VALUE)
public String duplicateXml() {
    return "<message>Duplicate</message>";
}
    
@GetMapping(value = "foos/duplicate", produces = MediaType.APPLICATION_JSON_VALUE)
public String duplicateJson() {
    return "{\"message\":\"Duplicate\"}";
}

其他方案:修改 URL 路径。

7. 新型请求映射快捷注解

Spring 4.3 引入基于 @RequestMapping 的快捷注解:

  • @GetMapping
  • @PostMapping
  • @PutMapping
  • @DeleteMapping
  • @PatchMapping

这些注解提升可读性,减少代码冗余。示例(CRUD API):

@GetMapping("/{id}")
public ResponseEntity<?> getBazz(@PathVariable String id){
    return new ResponseEntity<>(new Bazz(id, "Bazz"+id), HttpStatus.OK);
}

@PostMapping
public ResponseEntity<?> newBazz(@RequestParam("name") String name){
    return new ResponseEntity<>(new Bazz("5", name), HttpStatus.OK);
}

@PutMapping("/{id}")
public ResponseEntity<?> updateBazz(
  @PathVariable String id,
  @RequestParam("name") String name) {
    return new ResponseEntity<>(new Bazz(id, name), HttpStatus.OK);
}

@DeleteMapping("/{id}")
public ResponseEntity<?> deleteBazz(@PathVariable String id){
    return new ResponseEntity<>(new Bazz(id), HttpStatus.OK);
}

8. Spring 配置

假设 FooController 在以下包中:

package org.baeldung.spring.web.controller;

@Controller
public class FooController { ... }

只需 @Configuration 类启用 MVC 并扫描控制器:

@Configuration
@EnableWebMvc
@ComponentScan({ "org.baeldung.spring.web.controller" })
public class MvcConfig {
    //
}

9. 总结

本文深入探讨了 Spring 的 @RequestMapping 注解,涵盖基础用法、HTTP 请求头映射、@PathVariable 绑定 URI 部分、@RequestParam 处理参数等核心场景。

想学习 Spring MVC 另一核心注解?可探索 @ModelAttribute 注解详解


原始标题:Spring @RequestMapping | Baeldung