The @PathVariable annotation is used with a handler method parameter to capture the value of a URI template variable.
Definition of PathVariableVersion: 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
}
@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.5 Version compatibilities of spring-webmvc with this example: 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
|