Close

Spring MVC - @PathVariable Examples - Binding URI template variable with @PathVariable

[Last Updated: Mar 4, 2026]

The @PathVariable annotation is used with a handler method parameter to capture the value of a URI template variable.



Definition of PathVariable

Version: 7.0.5
 package org.springframework.web.bind.annotation;
 @Target(ElementType.PARAMETER)
 @Retention(RetentionPolicy.RUNTIME)
 @Documented
 public @interface PathVariable {
     @AliasFor("name")
     String value() default ""; 1
     @AliasFor("value")
     String name() default ""; 2
     boolean required() default true; 3
 }
1Alias for #name.
2The name of the path variable to bind to. (Since 4.3.3)
3Whether the path variable is required. (Since 4.3.3)


@PathVariable with variable name

@PathVariable element value/name can be used to define URI template variable name.

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

    @RequestMapping("{id}")
    public String handleRequest (@PathVariable("id") String userId, Model map) {
        map.addAttribute("msg", "User id " + userId);
        return "my-page";
    }
}




@PathVariable without variable name

@PathVariable 'value' element is optional. If the URI template variable name matches the method parameter name we can skip the variable name. This only works if the code is compiled with debugging information, Spring MVC will match the method parameter name to the URI template variable name.:

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

    @RequestMapping("profiles/{userName}")
    public String handleRequest2 (@PathVariable String userName, Model model) {
        model.addAttribute("msg", "user profile name : " + userName);
        return "my-page";
    }
}



Using multiple @PathVariable annotations

A method can have any number of @PathVariable annotations:

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

    @RequestMapping("{id}/posts/{postId}")
    public String handleRequest3 (@PathVariable("id") String userId,
                                  @PathVariable("postId") String postId,
                                  Model model) {
        model.addAttribute("msg", "user id : " + userId + ", post id: " + postId);
        return "my-page";

    }
}




Using Map with @PathVariable for multiple variables

If the method parameter is Map or MultiValueMap then the map is populated with all path variable names and values.

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

    @RequestMapping("{id}/messages/{msgId}")
    public String handleRequest4 (@PathVariable Map<String, String> varsMap, Model model) {
        model.addAttribute("msg", varsMap.toString());
        return "my-page";
    }
}



Template variables with different names are ambiguous (same base URI)

Defining different template variable names does not define different URI paths. Following example will cause runtime exception, complaining about ambiguous mapping.

@Controller
@RequestMapping("/employees")
public class EmployeeController {

    @RequestMapping("{id}")
    public String handleRequest(@PathVariable("id") String userId, Model model){
        model.addAttribute("msg", "employee id: "+userId);
        return "my-page";
    }

   @RequestMapping("{employeeName}")
    public String handleRequest2 (@PathVariable("employeeName") String userName,
                                                                   Model model) {
        model.addAttribute("msg", "employee name : " + userName);
        return "my-page";
    }
}
java.lang.IllegalStateException: Ambiguous handler methods mapped for HTTP path 'http://localhost/employees/234': {public java.lang.String com.logicbig.example.EmployeeController.handleRequest2(java.lang.String,org.springframework.ui.Model), public java.lang.String com.logicbig.example.EmployeeController.handleRequest(java.lang.String,org.springframework.ui.Model)}
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.lookupHandlerMethod(AbstractHandlerMethodMapping.java:375)
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.getHandlerInternal(AbstractHandlerMethodMapping.java:322)
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.getHandlerInternal(AbstractHandlerMethodMapping.java:60)




Using Regex in template variables to avoid ambiguity (same base URI)

If we use mutually exclusive regex in @RequestMapping then Spring can select one path depending on the request, even if their base URL path are same (like the last example, where we had ambiguous mapping exception)

/**
 * Using mutually exclusive regex, which can be used
 * to avoid ambiguous mapping exception
 */
@Controller
@RequestMapping("/dept")
public class DeptController {

    @RequestMapping("{id:[0-9]+}")
    public String handleRequest(@PathVariable("id") String userId, Model model){
        model.addAttribute("msg", "profile id: "+userId);
        return "my-page";

    }

    @RequestMapping("{name:[a-zA-Z]+}")
    public String handleRequest2 (@PathVariable("name") String deptName, Model model) {
        model.addAttribute("msg", "dept name : " + deptName);
        return "my-page";
    }
}

Using @PathVariable#required=false

By default, path variables are required, meaning Spring will throw an exception if the variable is missing from the URL. Setting required = false makes the path variable optional — when the segment is absent, the parameter is simply set to null. To use this properly, you must map both URL patterns (e.g., /users/profile and /users/profile/{id}) on the same handler method, and use a nullable wrapper type like Long instead of a primitive like long. This is useful when a single endpoint needs to handle both "get current/default user" and "get user by ID" scenarios without duplicating logic.

    @RequestMapping({"/profile", "/profile/{id}"})
    public String getUserProfile(
            @PathVariable(value = "id", required = false) Long userId,
            Model model) {

        if (userId == null) {
            model.addAttribute("msg", "No user id provided");

        } else {
            // ID provided → return that specific user's profile
            model.addAttribute("msg",
                               "user id: " + userId);
        }

Using template URI at class level

Template URI can also be specified in @RequestMapping at controller class level. Check out an example here.




Example Project

To test controllers run the unit tests in MyMvcControllerTest

Or you can run the app using embedded jetty:

mvn  clean jetty:run

Then try following urls:
http://localhost:8080/spring-mvc-path-variable/users/234
http://localhost:8080/spring-mvc-path-variable/users/profiles/joe
http://localhost:8080/spring-mvc-path-variable/users/profile
http://localhost:8080/spring-mvc-path-variable/users/234/posts/143
http://localhost:8080/spring-mvc-path-variable/users/234/messages/453
http://localhost:8080/spring-mvc-path-variable/users/234/posts/143
http://localhost:8080/spring-mvc-path-variable/dept/234
http://localhost:8080/spring-mvc-path-variable/dept/account

Dependencies and Technologies Used:

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

    Version compatibilities of spring-webmvc with this example:

      javax.servlet-api:3.x
    • 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
    • 7.0.0
    • 7.0.1
    • 7.0.2
    • 7.0.3
    • 7.0.4
    • 7.0.5

    Versions in green have been tested.

  • spring-test 7.0.5 (Spring TestContext Framework)
  • jakarta.servlet-api 6.1.0 (Jakarta Servlet API documentation)
  • junit-jupiter-engine 6.0.2 (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 Select All Download
  • spring-mvc-path-variable
    • src
      • main
        • java
          • com
            • logicbig
              • example
                • UserController.java
          • webapp
            • WEB-INF
              • views
        • test
          • java
            • com
              • logicbig
                • example

    See Also

    Join