How does Spring choose between multiple candidate @RequestMapping handlers, and what path pattern features (variables, wildcards) participate in matching?
answer
- RequestMappingInfo bundles all conditions
- literal > {var} > /* > /**
- More/more-specific predicates = more specific
- Equal best match -> IllegalStateException ambiguous
- PathPattern default in Boot 3; ** only at end
basics
~20 sWhen several mappings match, Spring picks the most specific one using RequestMappingInfo comparison: exact paths beat path variables beat wildcards, and more predicates (method, params, headers, produces) make a mapping more specific. Truly equal matches throw an ambiguous-mapping error.
solid answer
~50 sEach @RequestMapping becomes a RequestMappingInfo combining path patterns, method, params, headers, consumes, and produces conditions. RequestMappingHandlerMapping finds all matching infos for a request, then sorts them with a specificity comparator and picks the best. For paths, specificity ranks literal segments above single path variables ({id}) above single-segment wildcards (*) above the multi-segment ** wildcard; a pattern with fewer wildcards / URI variables wins. Predicates also count: a handler that additionally constrains produces or params outranks one that doesn't when the client matches. Modern Spring (5.3+, default in Boot 3) uses PathPattern parsing rather than the legacy AntPathMatcher. If two mappings are equally specific and both match, Spring raises IllegalStateException ("Ambiguous handler methods") — usually detectable as a startup or first-request failure. Understanding this prevents shadowing bugs where a broad wildcard swallows requests meant for a specific route.
code
java · 18 lines@RestController
@RequestMapping("/files")
public class FileController {
@GetMapping("/report.pdf") // literal - most specific
public Res exact() { ... }
@GetMapping("/{name}") // single URI variable
public Res byName(@PathVariable String name) { ... }
@GetMapping("/{id:\\d+}") // regex var: only numeric ids
public Res byId(@PathVariable long id) { ... }
@GetMapping("/**") // catch-all - least specific
public Res anything() { ... }
}
// GET /files/report.pdf -> exact(); GET /files/42 -> byId();
// GET /files/readme -> byName(); GET /files/a/b/c -> anything()go deeper
Not expected to detail specificity; may know exact paths beat wildcards.
Knows path-variable vs wildcard ordering and that ambiguity is an error.
Explains RequestMappingInfo conditions, specificity comparison, and regex-variable disambiguation.
Discusses PathPattern vs AntPathMatcher migration, trailing-slash/suffix-match removal, and API-design implications of route shadowing.
**The registry.** At startup `RequestMappingHandlerMapping` scans every `@Controller`/`@RestController`, turning each mapped method into a `RequestMappingInfo` — an immutable bundle of *conditions*: patterns, `RequestMethodsRequestCondition`, `ParamsRequestCondition`, `HeadersRequestCondition`, `ConsumesRequestCondition`, `ProducesRequestCondition`. **Matching a request.** For an incoming request, the handler mapping: 1. Filters to `RequestMappingInfo`s whose every condition matches (path pattern matches URL, method allowed, params/headers present, consumes matches Content-Type, produces matches Accept). 2. Sorts the survivors with `RequestMappingInfo.compareTo` using the request as context. 3. Selects the first (most specific). If the top two compare as *equal*, it throws `IllegalStateException: Ambiguous handler methods mapped ...`. **Path pattern specificity.** Whether using the newer `PathPattern` (Spring 5.3+, the default in Spring Boot 3) or legacy `AntPathMatcher`, the ordering intuition is the same, most-to-least specific: - Literal exact path: `/users/active` - Single URI template variable: `/users/{id}` - Single-segment wildcard: `/users/*` - Multi-segment wildcard: `/users/**` Among equally-typed patterns, fewer URI variables and fewer wildcards win; a longer literal prefix is more specific. `PathPattern` also supports `{*path}` to capture the remaining segments. **Predicates add specificity.** Given the same path, a mapping with more/more-specific conditions beats a barer one when the request satisfies them. E.g. two `/users` GET handlers, one with `produces=application/json`; a client sending `Accept: application/json` gets the JSON one because its `ProducesRequestCondition` is more specific for that request. Media-type conditions are compared by how closely they match the request's Accept/Content-Type. **Ambiguity failures.** If you declare `/users/{id}` and `/users/{name}` on the same verb with no other distinguishing condition, they are equally specific → ambiguous → `IllegalStateException`. The fix is to add a distinguishing predicate (regex in the variable like `/users/{id:\\d+}`, different params, or different produces). **PathPattern vs AntPathMatcher.** `PathPattern` (from spring-web) is faster, pre-parsed, and is now the default; `**` is only allowed at the end of a pattern. Legacy `AntPathMatcher` allowed `**` mid-pattern and did `?`/`*`/`**` glob matching at match time. The `useSuffixPatternMatch`/trailing-slash behaviors changed across versions — notably trailing-slash matching was deprecated and disabled by default in Spring 6, so `/users` no longer implicitly matches `/users/`. **Gotchas.** - A broad `@RequestMapping("/**")` catch-all can shadow specific routes if it's equally or more matchable — order doesn't save you; specificity does. - Trailing-slash: don't rely on `/path` matching `/path/` in Spring 6+. - `{id:\\d+}` regex variables narrow matches and break ambiguity ties. - Suffix pattern matching (`.json` extensions) is removed by default for security reasons. **When it matters.** Designing REST hierarchies, avoiding accidental route shadowing, and debugging `Ambiguous handler methods` or unexpected 404s.
- You mapped /users/{id} and /users/{name} on the same GET. What happens and how do you fix it?They are equally specific, so Spring throws IllegalStateException: Ambiguous handler methods. Fix by distinguishing them, e.g. a regex variable /users/{id:\\d+} for numeric ids, or different params/produces conditions.
- Does declaration order of handlers decide the winner when several match?No. Order is irrelevant; Spring sorts matches by specificity via RequestMappingInfo.compareTo. A more specific pattern or predicate wins regardless of where it is declared.
saying these in an interview costs you the question
- Believing handler declaration order determines which matches
- Assuming /** can never shadow specific routes
- Thinking /users still matches /users/ by default in Spring 6
- Not knowing equally-specific mappings throw at runtime/startup