Close

Spring MVC - Mapping Requests with @RequestMapping

[Last Updated: Mar 13, 2026]

The annotation @RequestMapping

@RequestMapping is used to map request URLs to specific controllers.

Definition of RequestMapping

Version: 7.0.5
 package org.springframework.web.bind.annotation;
 @Target({ ElementType.TYPE, ElementType.METHOD })
 @Retention(RetentionPolicy.RUNTIME)
 @Documented
 @Mapping
 @Reflective(ControllerMappingReflectiveProcessor.class)
 public @interface RequestMapping {
     String name() default ""; 1
     @AliasFor("path")
     String[] value() default {}; 2
     @AliasFor("value")
     String[] path() default {}; 3
     RequestMethod[] method() default {}; 4
     String[] params() default {}; 5
     String[] headers() default {}; 6
     String[] consumes() default {}; 7
     String[] produces() default {}; 8
     String version() default ""; 9
 }
1Assign a name to this mapping.
2The path mapping URIs — for example, "/profile".
3The path mapping URIs — for example, "/profile". (Since 4.2)
4The HTTP request methods to map to, narrowing the primary mapping: GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE.
5The parameters of the mapped request, narrowing the primary mapping.
6The headers of the mapped request, narrowing the primary mapping.
7Narrows the primary mapping by media types that can be consumed by the mapped handler.
8Narrows the primary mapping by media types that can be produced by the mapped handler.
9Narrows the primary mapping by an API version. (Since 7.0)

All elements of @RequestMapping annotation are optional.

It's annotated with @Target(value={METHOD,TYPE}), so it can be used on class level or method level.

If @RequestMapping specifies a path on class level then all paths in the methods are relative.

@RequestMapping on the class level is not required. Without it, all paths are simply absolute, and not relative

The handler methods without @RequestMapping won't be mapped, even their enclosing class has @Controller and a valid @RequestMapping annotations.


Elements of @RequestMapping

String[] value

It's the URL mapping expression:

@RequestMapping("/users")
 @Controller
 public class UserController{
    ...
 }


Multiple URLs can be specified:

@RequestMapping({"/users", "/clients"})
 @Controller
 public class UserController{
    ...
 }

It may contain URI templates:

@RequestMapping("/users")
 @Controller
 public class UserController{
   @RequestMapping("/{userId}")
    public String handle(....){
     ....
    }
 }

The method handle() will map to /users/{userId}



URI Template Patterns may contain regex:

@RequestMapping("/{userId:[0-9]+}")


URI templates can be captured by handler's method parameter annotated with @PathVariable:

@RequestMapping("/{userId}")
    public void handle(@PathVariable("userId") String userId) {
            // ...
    }


A method without @RequestMapping won't be mapped, even though enclosing class annotations are valid. For example the following code will not map the method handleAllUsersRequest() and will return 404 error for the request /users:

@Controller
 @RequestMapping("/users")
 public class UserController {

  public String handleAllUsersRequest(){
        .....
  }
 }

To fix above mapping (for the request /users):

@Controller
 @RequestMapping("/users")
 public class UserController {

  @RequestMapping
  public String handleAllUsersRequest(){
        .....
  }
 }


On class level @RequestMapping("") or just @RequestMapping will map to root url "/" (example here).

If on class level no @RequestMapping annotation is used then on method level @RequestMapping("") will map to the root url ("/"), whereas, empty @RequestMapping (without path) will act as a fall back and will map to all URLs for which specific matches are not available (example here).



RequestMethod[] method

The HTTP request methods, this handler can support:

@Controller
@RequestMapping("/users")
public class UserController {

   @RequestMapping(value= "{id}", method = {RequestMethod.GET, RequestMethod.DELETE})
   public String handle(......){
     //..
   }
}


Per good design principles, it's better to define separate handler methods based on HTTP methods.

Different handler methods can uniquely be defined based on different HTTP methods (no ambiguity), even if they have same request URL path:

@Controller
@RequestMapping("/users")
public class UserController {

   @RequestMapping(value= "{id}", method = {RequestMethod.GET})
   public String handleGet(.....){
     //..
   }

   @RequestMapping(value= "{id}", method = {RequestMethod.DELETE})
   public String handleDelete(.....){
     //..
   }


}



String[] params

The query string parameters. The annotated method will only be mapped if the query string matches.

This is another level for defining handler methods uniquely (no ambiguity) or in other words to narrow down the primary mapping. In the following example the request /users?id=4 will be mapped to method handleUserId4 and the request /user?id=10 will be mapped to method handleUserId10:

@Controller
@RequestMapping("/users")
public class UserControllerParams {

    @RequestMapping(params = "id=4")
    public String handleUserId4(.....) {
        System.out.println("got param id = 4");
        return "view-name";
    }

    @RequestMapping(params = "id=10")
    public String handleUserId10(....) {
        System.out.println("got param id = 10");
        return "view-name";
    }
}

We don't have to necessarily capture the query param using @RequestParam as each method will only be called when params matches, that means we can safely use hardcoded param values inside the handler method.

Defining multiple query params:

@Controller
@RequestMapping("/users")
public class UserControllerParams {

    @RequestMapping(params = {"state=TX", "dept=IT"})
    public String handleRequest(.....) {
         ....
        return "view-name";
    }
}

We can skip the query param value part. In that case all request having the specified param name will be mapped regardless of their values:

@Controller
@RequestMapping("/users")
public class UserControllerParams {

    @RequestMapping(params = "dept")
    public String handleRequest(.....) {
         ....
        return "view-name";
    }
}


String[] headers

It's just like params element but instead of query params, it is used to specify HTTP headers key-value pair. That uniquely defines handlers (narrowing the primary mapping):

@Controller
@RequestMapping("/users")
public class UserControllerHeader {

    @RequestMapping(headers = "id=4")
    public String handleAllUsersRequest() {
        System.out.println("got header id = 4");
        return "view-name";
    }

    @RequestMapping(headers = "id=10")
    public String handleAllUsersRequest2() {
        System.out.println("got header id = 10");
        return "view-name";
    }

}



String[] consumes

It defines the consumable media types of the mapped request, narrowing the primary mapping.

It's based on Content Negotiation specifications.

@Controller
@RequestMapping("/users")
public class UserControllerConsume {

    @RequestMapping(consumes = MediaType.APPLICATION_JSON_VALUE)
    public String handleJson(@RequestBody String s) {
        System.out.println("json body : " + s);
        return "view-name";
    }

    @RequestMapping(consumes = MediaType.APPLICATION_XML_VALUE)
    public String handleXML(@RequestBody String s) {
        System.out.println("xml body " + s);
        return "view-name";
    }
}

Expressions can be negated by using the "!" operator, as in "!text/plain", which matches all requests with a Content-Type other than "text/plain".

Mapping Requests with @RequestMapping

What is @RequestBody?

Annotation indicating a method parameter should be bound to the body of the web request. The body of the request is passed through an HttpMessageConverter to resolve the method argument depending on the content type of the request.




String[] produces

The producible media types of the mapped request, narrowing the primary mapping.

@Controller
@RequestMapping("/users")
public class UserControllerProduce {

    @RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE)
     public  @ResponseBody String handleJson() {
        System.out.println("Got json request");
        return "{ \"userName\": \"Joe\"}";
    }

    @RequestMapping(produces = MediaType.APPLICATION_XML_VALUE)
    public @ResponseBody  String handleXML() {
        System.out.println("Got xml request");
        return "<userName>Joe</userName>";
    }
}

Expressions can be negated by using the "!" operator, as in "!text/plain", which matches all requests with a Accept other than "text/plain".




String[] path

Alias for 'value'

Ant-style path patterns are also supported (e.g. /**/users).

This example will match any URL ending with users e.g. /dept1/dept2/dept3/dept4/dept5/users

@Controller
@RequestMapping("/**/users")
public class UserControllerPath {

    @RequestMapping
    public void handleAllUsersRequest(HttpServletRequest request){
        System.out.println(request.getRequestURL());
    }
}

Also check out this to understand URI pattern matching in details.




String name

Assign a name to this mapping.



Placeholders in path pattern

@RequestMapping's value element can also have a placeholder ${...} pattern against a property source. Check out an example here.



String version (Spring 7+ new @RequestMapping element)

Spring Framework 7 introduces native, declarative API versioning support.

Versioning is implemented via a version attribute in @RequestMapping and related annotations (@GetMapping, @PostMapping, etc.).

Spring 7 introduces a declarative API versioning model that replaces manual path or header handling. By using the version attribute within @RequestMapping, developers can natively manage multiple API iterations.

  • Flexible Resolution: Versions can be automatically extracted from URL paths, Request Headers, query params or Media Types.
  • Global Configuration: The ApiVersionConfigurer allows for setting default versions and handling Deprecation or Sunset headers. query param.
  • Tooling Integration: Improved support for Spring Doc and OpenAPI to automatically generate version-specific documentation.
@EnableWebMvc
@Configuration
public class WebConfigApiVersioning implements WebMvcConfigurer {
    
    .....
 
    @Override
    public void configureApiVersioning(ApiVersionConfigurer configurer) {
        configurer.useQueryParam("v")
                  .addSupportedVersions("2.0", "3.0", "3.1");
    }
}
    @RequestMapping(version = "2")
    public String handleAllUsersRequest(){
       .....
    }

    @RequestMapping(version = "3+")
    public String handleAllUsersRequest3(){
       ..
    }

Unit Tests

Let's run unit tests (included in the project browser below)

$ mvn clean test -Dtest=*
[INFO] Scanning for projects...
[INFO]
[INFO] ------------< com.logicbig.com:spring-mvc-request-mapping >-------------
[INFO] Building spring-mvc-request-mapping 1.0-SNAPSHOT
[INFO] from pom.xml
[INFO] --------------------------------[ war ]---------------------------------
[INFO]
[INFO] --- clean:3.2.0:clean (default-clean) @ spring-mvc-request-mapping ---
[INFO] Deleting D:\example-projects\spring-mvc\spring-mvc-request-mapping\target
[INFO]
[INFO] --- resources:3.3.1:resources (default-resources) @ spring-mvc-request-mapping ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory D:\example-projects\spring-mvc\spring-mvc-request-mapping\src\main\resources
[INFO]
[INFO] --- compiler:3.15.0:compile (default-compile) @ spring-mvc-request-mapping ---
[INFO] Recompiling the module because of changed source code.
[INFO] Compiling 18 source files with javac [debug target 25] to target\classes
[INFO]
[INFO] --- resources:3.3.1:testResources (default-testResources) @ spring-mvc-request-mapping ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory D:\example-projects\spring-mvc\spring-mvc-request-mapping\src\test\resources
[INFO]
[INFO] --- compiler:3.15.0:testCompile (default-testCompile) @ spring-mvc-request-mapping ---
[INFO] Recompiling the module because of changed dependency.
[INFO] Compiling 9 source files with javac [debug target 25] to target\test-classes
[INFO]
[INFO] --- surefire:3.2.5:test (default-test) @ spring-mvc-request-mapping ---
[INFO] Using auto detected provider org.apache.maven.surefire.junitplatform.JUnitPlatformProvider
[WARNING] file.encoding cannot be set as system property, use <argLine>-Dfile.encoding=...</argLine> instead
[INFO]
[INFO] -------------------------------------------------------
[INFO] T E S T S
[INFO] -------------------------------------------------------
[INFO] Running com.logicbig.example.UserControllerTest404
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.185 s -- in com.logicbig.example.UserControllerTest404
[INFO] Running com.logicbig.example.UserControllerTestConsume
xml body <user><userName>Joe</userName></user>
json body : { "userName": "Joe"}
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.406 s -- in com.logicbig.example.UserControllerTestConsume
[INFO] Running com.logicbig.example.UserControllerTestHeader
got header id = 10
got header id = 4
[INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.078 s -- in com.logicbig.example.UserControllerTestHeader
[INFO] Running com.logicbig.example.UserControllerTestHttpMethod
in get method
in delete method
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.075 s -- in com.logicbig.example.UserControllerTestHttpMethod
[INFO] Running com.logicbig.example.UserControllerTestParams
got param id = 10
got param id = 4
[INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.090 s -- in com.logicbig.example.UserControllerTestParams
[INFO] Running com.logicbig.example.UserControllerTestPath
http://localhost/dept1/dept2/dept3/dept4/dept5/users
http://localhost/dept/account/users
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.054 s -- in com.logicbig.example.UserControllerTestPath
[INFO] Running com.logicbig.example.UserControllerTestProduce
Got xml request
Got json request
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.298 s -- in com.logicbig.example.UserControllerTestProduce
[INFO] Running com.logicbig.example.UserControllerTestRegex
user id alphas abc
user id digits 243
[INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.082 s -- in com.logicbig.example.UserControllerTestRegex
[INFO] Running com.logicbig.example.UserControllerTestVersioning
version 3+

MockHttpServletRequest:
HTTP Method = GET
Request URI = /users
Parameters = {v=[3.1]}
Headers = []
Body = <no character encoding set>
Session Attrs = {}

Handler:
Type = com.logicbig.example.UserControllerVersioning
Method = com.logicbig.example.UserControllerVersioning#handleAllUsersRequest3()

Async:
Async started = false
Async result = null

Resolved Exception:
Type = null

ModelAndView:
View name =
View = null
Model = null

FlashMap:
Attributes = null

MockHttpServletResponse:
Status = 200
Error message = null
Headers = [Content-Language:"en"]
Content type = null
Body =
Forwarded URL =
Redirected URL = null
Cookies = []
version 2
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.114 s -- in com.logicbig.example.UserControllerTestVersioning
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 20, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 8.502 s
[INFO] Finished at: 2026-03-13T22:26:09+08:00
[INFO] ------------------------------------------------------------------------

Example Project

Dependencies and Technologies Used:

  • spring-webmvc 7.0.6 (Spring Web MVC)
     Version Compatibility: 3.2.9.RELEASE - 7.0.6Version List
    ×

    Version compatibilities of spring-webmvc with this example:

      javax.servlet-api:3.x
    • 3.2.9.RELEASE
    • 3.2.10.RELEASE
    • 3.2.11.RELEASE
    • 3.2.12.RELEASE
    • 3.2.13.RELEASE
    • 3.2.14.RELEASE
    • 3.2.15.RELEASE
    • 3.2.16.RELEASE
    • 3.2.17.RELEASE
    • 3.2.18.RELEASE
    • 4.0.0.RELEASE
    • 4.0.1.RELEASE
    • 4.0.2.RELEASE
    • 4.0.3.RELEASE
    • 4.0.4.RELEASE
    • 4.0.5.RELEASE
    • 4.0.6.RELEASE
    • 4.0.7.RELEASE
    • 4.0.8.RELEASE
    • 4.0.9.RELEASE
    • 4.1.0.RELEASE
    • 4.1.1.RELEASE
    • 4.1.2.RELEASE
    • 4.1.3.RELEASE
    • 4.1.4.RELEASE
    • 4.1.5.RELEASE
    • 4.1.6.RELEASE
    • 4.1.7.RELEASE
    • 4.1.8.RELEASE
    • 4.1.9.RELEASE
    • 4.2.0.RELEASE
    • 4.2.1.RELEASE
    • 4.2.2.RELEASE
    • 4.2.3.RELEASE
    • 4.2.4.RELEASE
    • 4.2.5.RELEASE
    • 4.2.6.RELEASE
    • 4.2.7.RELEASE
    • 4.2.8.RELEASE
    • 4.2.9.RELEASE
    • 4.3.0.RELEASE
    • 4.3.1.RELEASE
    • 4.3.2.RELEASE
    • 4.3.3.RELEASE
    • 4.3.4.RELEASE
    • 4.3.5.RELEASE
    • 4.3.6.RELEASE
    • 4.3.7.RELEASE
    • 4.3.8.RELEASE
    • 4.3.9.RELEASE
    • 4.3.10.RELEASE
    • 4.3.11.RELEASE
    • 4.3.12.RELEASE
    • 4.3.13.RELEASE
    • 4.3.14.RELEASE
    • 4.3.15.RELEASE
    • 4.3.16.RELEASE
    • 4.3.17.RELEASE
    • 4.3.18.RELEASE
    • 4.3.19.RELEASE
    • 4.3.20.RELEASE
    • 4.3.21.RELEASE
    • 4.3.22.RELEASE
    • 4.3.23.RELEASE
    • 4.3.24.RELEASE
    • 4.3.25.RELEASE
    • 4.3.26.RELEASE
    • 4.3.27.RELEASE
    • 4.3.28.RELEASE
    • 4.3.29.RELEASE
    • 4.3.30.RELEASE
    • 5.0.0.RELEASE
    • 5.0.1.RELEASE
    • 5.0.2.RELEASE
    • 5.0.3.RELEASE
    • 5.0.4.RELEASE
    • 5.0.5.RELEASE
    • 5.0.6.RELEASE
    • 5.0.7.RELEASE
    • 5.0.8.RELEASE
    • 5.0.9.RELEASE
    • 5.0.10.RELEASE
    • 5.0.11.RELEASE
    • 5.0.12.RELEASE
    • 5.0.13.RELEASE
    • 5.0.14.RELEASE
    • 5.0.15.RELEASE
    • 5.0.16.RELEASE
    • 5.0.17.RELEASE
    • 5.0.18.RELEASE
    • 5.0.19.RELEASE
    • 5.0.20.RELEASE
    • 5.1.0.RELEASE
    • 5.1.1.RELEASE
    • 5.1.2.RELEASE
    • 5.1.3.RELEASE
    • 5.1.4.RELEASE
    • 5.1.5.RELEASE
    • 5.1.6.RELEASE
    • 5.1.7.RELEASE
    • 5.1.8.RELEASE
    • 5.1.9.RELEASE
    • 5.1.10.RELEASE
    • 5.1.11.RELEASE
    • 5.1.12.RELEASE
    • 5.1.13.RELEASE
    • 5.1.14.RELEASE
    • 5.1.15.RELEASE
    • 5.1.16.RELEASE
    • 5.1.17.RELEASE
    • 5.1.18.RELEASE
    • 5.1.19.RELEASE
    • 5.1.20.RELEASE
    • 5.2.0.RELEASE
    • 5.2.1.RELEASE
    • 5.2.2.RELEASE
    • 5.2.3.RELEASE
    • 5.2.4.RELEASE
    • 5.2.5.RELEASE
    • 5.2.6.RELEASE
    • 5.2.7.RELEASE
    • 5.2.8.RELEASE
    • 5.2.9.RELEASE
    • 5.2.10.RELEASE
    • 5.2.11.RELEASE
    • 5.2.12.RELEASE
    • 5.2.13.RELEASE
    • 5.2.14.RELEASE
    • 5.2.15.RELEASE
    • 5.2.16.RELEASE
    • 5.2.17.RELEASE
    • 5.2.18.RELEASE
    • 5.2.19.RELEASE
    • 5.2.20.RELEASE
    • 5.2.21.RELEASE
    • 5.2.22.RELEASE
    • 5.2.23.RELEASE
    • 5.2.24.RELEASE
    • 5.2.25.RELEASE
    • 5.3.0
    • 5.3.1
    • 5.3.2
    • 5.3.3
    • 5.3.4
    • javax.servlet-api:4.x
    • 5.3.5
    • 5.3.6
    • 5.3.7
    • 5.3.8
    • 5.3.9
    • 5.3.10
    • 5.3.11
    • 5.3.12
    • 5.3.13
    • 5.3.14
    • 5.3.15
    • 5.3.16
    • 5.3.17
    • 5.3.18
    • 5.3.19
    • 5.3.20
    • 5.3.21
    • 5.3.22
    • 5.3.23
    • 5.3.24
    • 5.3.25
    • 5.3.26
    • 5.3.27
    • 5.3.28
    • 5.3.29
    • 5.3.30
    • 5.3.31
    • 5.3.32
    • 5.3.33
    • 5.3.34
    • 5.3.35
    • 5.3.36
    • 5.3.37
    • 5.3.38
    • 5.3.39
    • javax.* -> jakarta.*
      jakarta.servlet-api:6.x
      Java 17 min
    • 6.0.0
    • 6.0.1
    • 6.0.2
    • 6.0.3
    • 6.0.4
    • 6.0.5
    • 6.0.6
    • 6.0.7
    • 6.0.8
    • 6.0.9
    • 6.0.10
    • 6.0.11
    • 6.0.12
    • 6.0.13
    • 6.0.14
    • 6.0.15
    • 6.0.16
    • 6.0.17
    • 6.0.18
    • 6.0.19
    • 6.0.20
    • 6.0.21
    • 6.0.22
    • 6.0.23
    • 6.1.0
    • 6.1.1
    • 6.1.2
    • 6.1.3
    • 6.1.4
    • 6.1.5
    • 6.1.6
    • 6.1.7
    • 6.1.8
    • 6.1.9
    • 6.1.10
    • 6.1.11
    • 6.1.12
    • 6.1.13
    • 6.1.14
    • 6.1.15
    • 6.1.16
    • 6.1.17
    • 6.1.18
    • 6.1.19
    • 6.1.20
    • 6.1.21
    • 6.2.0
    • 6.2.1
    • 6.2.2
    • 6.2.3
    • 6.2.4
    • 6.2.5
    • 6.2.6
    • 6.2.7
    • 6.2.8
    • 6.2.9
    • 6.2.10
    • 6.2.11
    • 6.2.12
    • 6.2.13
    • 6.2.14
    • 6.2.15
    • 6.2.16
    • 6.2.17
    • 7.0.0
    • 7.0.1
    • 7.0.2
    • 7.0.3
    • 7.0.4
    • 7.0.5
    • 7.0.6

    Versions in green have been tested.

  • spring-test 7.0.6 (Spring TestContext Framework)
  • json-path 2.10.0 (A library to query and verify JSON)
  • jakarta.servlet-api 6.1.0 (Jakarta Servlet API documentation)
  • junit-jupiter-engine 6.0.3 (Module "junit-jupiter-engine" of JUnit)
  • hamcrest 3.0 (Core API and libraries of hamcrest matcher framework)
  • JDK 25
  • Maven 3.9.11

Spring MVC - @RequestMapping Examples Select All Download
  • spring-mvc-request-mapping
    • src
      • main
        • java
          • com
            • logicbig
              • example
                • UserControllerConsume.java
        • test
          • java
            • com
              • logicbig
                • example

    See Also

    Join