Close

Spring MVC - Understanding HandlerMapping

[Last Updated: Sep 23, 2026]

The interface HandlerMapping maps requested URLs to handler methods/objects (such as classes annotated with @Controller together with their @RequestMapping-annotated methods).

Definition of HandlerMapping

Version: 7.0.6
 package org.springframework.web.servlet;
 public interface HandlerMapping {
     default boolean usesPathPatterns(); 1
     HandlerExecutionChain getHandler(HttpServletRequest request)
                                      throws Exception; 2
 }
1Whether this HandlerMapping instance has been enabled to use parsed org.springframework.web.util.pattern.PathPatterns in which case the DispatcherServlet automatically org.springframework.web.util.ServletRequestPathUtils#parseAndCache the RequestPath to make it available for org.springframework.web.util.ServletRequestPathUtils#getParsedRequestPath in HandlerMappings, HandlerInterceptors, and other components. (Since 5.3)
2Return a handler and any interceptors for this request.

DispatcherServlet processes one or more HandlerMappings (in a specified order) to forward the request to the mapped handler object. For a request, once a HandlerMapping returns a non-null HandlerExecutionChain instance, it is used for the next step (i.e. invoking the handler), and no further HandlerMapping objects are attempted. According to DispatcherServlet.properties, the following handlers are registered by default:

  • BeanNameUrlHandlerMapping:

    Maps from URLs to beans (the controllers) by bean names. This only works if the URL starts with "/"

  • DefaultAnnotationHandlerMapping:

    This was a legacy handler mapping that matched URLs to handler classes/methods via the @RequestMapping value element. It was deprecated back in Spring 3.1 in favor of RequestMappingHandlerMapping, and it has since been removed completely (as of Spring Framework 5.0). It is mentioned here only for historical context — a current DispatcherServlet.properties (or an @EnableWebMvc/Spring Boot configuration) will not register it. More info here (archived docs).

  • RequestMappingHandlerMapping:

    RequestMappingHandlerMapping is the HandlerMapping implementation that powers Spring MVC's annotation-based controllers. It scans your @Controller (and @RestController) beans, reads the @RequestMapping annotations (and its shortcuts like @GetMapping, @PostMapping, etc.) on their methods, and builds a map from URL patterns (plus HTTP method, headers, params, etc.) to the matching handler method.

  • RouterFunctionMapping:

    It routes requests using functional RouterFunction beans instead of @RequestMapping-annotated methods. It detects and composes all RouterFunction beans in the context, matching each request to its handler function. Available in both Spring MVC (since 5.2) and WebFlux.

We can also print all registered handlers during runtime:

package com.logicbig.example;

import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.HandlerMapping;
import java.util.Comparator;
import java.util.Map;

@Controller
public class TestController {

    @Autowired
    ApplicationContext context;

    @RequestMapping(value = "/test")
    @ResponseBody
    public String handleRequest() {
        Map<String, HandlerMapping> matchingBeans =
                BeanFactoryUtils.beansOfTypeIncludingAncestors(
                        context,
                        HandlerMapping.class,
                        true,
                        false);

        matchingBeans
                .entrySet()
                .stream()
                .sorted(Comparator.comparingInt((Map.Entry<String, HandlerMapping> e) ->
                                                        ((Ordered) e.getValue()).getOrder())
                                  .thenComparing(Map.Entry::getKey))
                .forEach(e -> System.out.printf("order:%s %s=%s%n",
                                                ((Ordered) e.getValue()).getOrder(),
                                                e.getKey(),
                                                e.getValue().getClass()
                                                 .getSimpleName()));
        return "response from /test";
    }
}

Running The Example

To try examples, run embedded Jetty (configured in pom.xml of example project below):

mvn jetty:run

Accessing http://localhost:8080/test will print the list t on the server console:

$ curl -s "http://localhost:8080/test"
response from /test

Server Output


order:-1 routerFunctionMapping=RouterFunctionMapping
order:0 requestMappingHandlerMapping=RequestMappingHandlerMapping
order:2 beanNameHandlerMapping=BeanNameUrlHandlerMapping

The above list differs from DispatcherServlet.properties because we are using @EnableWebMvc in our configuration class:

@EnableWebMvc
@Configuration
@ComponentScan
public class AppConfig {
}

@EnableWebMvc imports all configuration from DelegatingWebMvcConfiguration (a subclass of WebMvcConfigurationSupport). This configuration includes the registration of HandlerMappings. Check out this tutorial as well.

Note that we also printed the order of each handler. DispatcherServlet processes the handlers in the order provided by the Ordered interface (Lower numbers are processed first).

EmptyHandlerMapping (as seen in the above output) is a static nested class inside WebMvcConfigurationSupport whose getHandler() always returns null. It is processed last, if none of the other mappings matched (its order is set to Integer.MAX_VALUE).

Using RequestMappingHandlerMapping

To have RequestMappingHandlerMapping map our handler methods, we need to use the @RequestMapping annotation on the @Controller class. Throughout this series of tutorials, we have mostly been using this same HandlerMapping. The TestController example above also uses this annotation.

Using BeanNameUrlHandlerMapping

This maps requests to controller beans by their bean names. See an example here.

HandlerMapping settings

Since all default HandlerMapping implementations extend AbstractHandlerMapping, they share the following important common properties:

  • setInterceptors(Object... interceptors) Sets interceptors to apply to all matched mappings. Supported interceptor types are HandlerInterceptor, WebRequestInterceptor, and MappedInterceptor.

  • setOrder(int order) All registered handler mappings are sorted by this order (ascending). The mapping used is the first one that matches.

  • setAlwaysUseFullPath(boolean b) If set to true, the application must use the full mapping path, including the part specified by the parent servlet mapping. The default is false. See an example here.

  • setDefaultHandler(Object defaultHandler) A default handler to use when this mapping does not match the request. Its default value is null. See an example here.

How to customize a default HandlerMapping?

To customize one of the default HandlerMappings, we can register it as a @Bean using exactly the same name that WebMvcConfigurationSupport uses — but the drawback of this approach is that we have to set all of its properties from scratch. A better approach is to implement WebMvcConfigurer and override the relevant method that configures the HandlerMapping we want (e.g. WebMvcConfigurer#configurePathMatch()); this is also the standard customization point in Spring Boot applications. If neither option works, we can avoid @EnableWebMvc altogether and extend WebMvcConfigurationSupport directly, overriding the @Bean factory methods that create the default HandlerMapping beans. Check out this tutorial too.

Example Project

Dependencies and Technologies Used:

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

    Version compatibilities of spring-webmvc with this example:

      javax.servlet-api:3.x
    • 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
    • 6.2.18
    • 6.2.19
    • 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.

  • jakarta.servlet-api 6.1.0 (Jakarta Servlet API documentation)
  • JDK 25
  • Maven 3.9.11

Spring MVC - HandlerMapping List Select All Download
  • spring-handler-mapping
    • src
      • main
        • java
          • com
            • logicbig
              • example
                • TestController.java

    See Also

    Join