Course – LS – All

Get started with Spring and Spring Boot, through the Learn Spring course:

>> CHECK OUT THE COURSE

1. Introduction

In Spring MVC, the DispatcherServlet acts as front controller – receiving all incoming HTTP requests and processing them.

Simply put, the processing occurs by passing the requests to the relevant component with the help of handler mappings.

HandlerMapping is an interface that defines a mapping between requests and handler objects. While Spring MVC framework provides some ready-made implementations, the interface can be implemented by developers to provide customized mapping strategy.

This article discusses some of the implementations provided by Spring MVC namely BeanNameUrlHandlerMapping, SimpleUrlHandlerMapping, ControllerClassNameHandlerMapping, their configuration, and the differences between them.

2. BeanNameUrlHandlerMapping

BeanNameUrlHandlerMapping is the default HandlerMapping implementation. BeanNameUrlHandlerMapping maps request URLs to beans with the same name.

This particular mapping has support for direct name matching and also for pattern matching using the “*” pattern.

For example, an incoming URL “/foo” maps to a bean called “/foo”. An example of pattern mapping is mapping requests to “/foo*” to beans with names starting with “/foo” like “/foo2/” or “/fooOne/”.

Let’s configure this example here and register a bean controller that handles requests to “/beanNameUrl”:

@Configuration
public class BeanNameUrlHandlerMappingConfig {
    @Bean
    BeanNameUrlHandlerMapping beanNameUrlHandlerMapping() {
        return new BeanNameUrlHandlerMapping();
    }

    @Bean("/beanNameUrl")
    public WelcomeController welcome() {
        return new WelcomeController();
    }
}

This is the XML equivalent of the above Java based config:

<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
<bean name="/beanNameUrl" class="com.baeldung.WelcomeController" />

It’s important to note that in both of these configurations, defining a bean for BeanNameUrlHandlerMapping is not required as it is provided by Spring MVC. Removing this bean definition will cause no problems and requests will still be mapped to their registered handler beans.

Now all requests to “/beanNameUrl” will be forwarded by DispatcherServlet to “WelcomeController“. WelcomeController returns a view name called “welcome“.

The following code tests this configuration and makes sure that the correct view name is returned:

public class BeanNameMappingConfigTest {
    // ...

    @Test
    public void whenBeanNameMapping_thenMappedOK() {
        mockMvc.perform(get("/beanNameUrl"))
          .andExpect(status().isOk())
          .andExpect(view().name("welcome"));
    }
}

3. SimpleUrlHandlerMapping

Next, the SimpleUrlHandlerMapping is the most flexible HandlerMapping implementation. It allows for direct and declarative mapping between either bean instances and URLs or between bean names and URLs.

Let’s map requests “/simpleUrlWelcome” and “/*/simpleUrlWelcome” to the “welcome” bean:

@Configuration
public class SimpleUrlHandlerMappingConfig {

    @Bean
    public SimpleUrlHandlerMapping simpleUrlHandlerMapping() {
        SimpleUrlHandlerMapping simpleUrlHandlerMapping
          = new SimpleUrlHandlerMapping();
        
        Map<String, Object> urlMap = new HashMap<>();
        urlMap.put("/simpleUrlWelcome", welcome());
        simpleUrlHandlerMapping.setUrlMap(urlMap);
        
        return simpleUrlHandlerMapping;
    }

    @Bean
    public WelcomeController welcome() {
        return new WelcomeController();
    }
}

Alternatively, here’s the equivalent XML configuration:

<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
    <property name="mappings">
        <value>
            /simpleUrlWelcome=welcome
            /*/simpleUrlWelcome=welcome
        </value>
    </property>
</bean>
<bean id="welcome" class="com.baeldung.WelcomeController" />

It’s important to note that in the XML configuration, a mapping between “<value>” tag must be done in a form accepted by java.util.Properties class and it should follow the syntax: path= Handler_Bean_Name.

The URL should normally be with a leading slash, however, if the path doesn’t begin with one, Spring MVC adds it automatically.

A different way to configure the above example in XML is to use the “props” property instead of “value”. Props have a list of “prop” tag where each defines a mapping where “key” referred to the mapped URL and the value of the tag is the name of the bean.

<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
    <property name="mappings">
        <props>
            <prop key="/simpleUrlWelcome">welcome</prop>
            <prop key="/*/simpleUrlWelcome">welcome</prop>
        </props>
    </property>
</bean>

The following test case makes sure that requests to “/simpleUrlWelcome” is handled by “WelcomeController” which returns a view name called “welcome” :

public class SimpleUrlMappingConfigTest {
    // ...

    @Test
    public void whenSimpleUrlMapping_thenMappedOK() {
        mockMvc.perform(get("/simpleUrlWelcome"))
          .andExpect(status().isOk())
          .andExpect(view().name("welcome"));
    }
}

4. ControllerClassNameHandlerMapping (removed in Spring 5)

The ControllerClassNameHandlerMapping maps URL to a registered controller bean (or a controller annotated with the @Controller annotation) that has, or starts with, the same name.

It can be more convenient in many scenarios especially for simple controller implementations that handle a single request type. The convention used by Spring MVC is to use the name of the class and remove the “Controller” suffix, then change the name to a lower case and return it as the mapping with a leading “/”.

For example “WelcomeController” would return as mapping to “/welcome*”, i.e. to any URL that starts with “welcome”.

Let’s configure ControllerClassNameHandlerMapping:

@Configuration
public class ControllerClassNameHandlerMappingConfig {

    @Bean
    public ControllerClassNameHandlerMapping controllerClassNameHandlerMapping() {
        return new ControllerClassNameHandlerMapping();
    }

    @Bean
    public WelcomeController welcome() {
        return new WelcomeController();
    }
}

Note that ControllerClassNameHandlerMapping is deprecated from Spring 4.3 in favor of annotation driven handler methods.

Another important note is that controller names will always be returned in lowercase (minus the “Controller” suffix). So if we have a controller called “WelcomeBaeldungController“, it will only handle requests to “/welcomebaeldung” and not to “/welcomeBaeldung”.

In both Java config and XML config below, we define ControllerClassNameHandlerMapping bean and register beans for the controllers that we will use to handle requests. We also register a bean of type “WelcomeController” and that bean will handle all requests that start with “/welcome”.

Here’s the equivalent XML configuration:

<bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping" />
<bean class="com.baeldung.WelcomeController" />

When using the above configuration, requests to “/welcome” will be handled by the “WelcomeController“.

The following code will make sure that requests to “/welcome*” such as “/welcometest” is handled by “WelcomeController” which returns a view name called “welcome“:

public class ControllerClassNameHandlerMappingTest {
    // ...

    @Test
    public void whenControllerClassNameMapping_thenMappedOK() {
        mockMvc.perform(get("/welcometest"))
          .andExpect(status().isOk())
          .andExpect(view().name("welcome"));
    }
}

5. Configuring Priorities

Spring MVC framework allows more than one implementation of HandlerMapping interface at the same time.

Let us create a configuration and register two controllers, both mapped to URL “/welcome”, only using different mapping and returning different view names:

@Configuration
public class HandlerMappingDefaultConfig {

    @Bean("/welcome")
    public BeanNameHandlerMappingController beanNameHandlerMapping() {
        return new BeanNameHandlerMappingController();
    }

    @Bean
    public WelcomeController welcome() {
        return new WelcomeController();
    }
}

With no explicit handler mapper registered, a default BeanNameHandlerMapping will be used. Let us assert this behaviour with the test:

@Test
public void whenConfiguringPriorities_thenMappedOK() {
    mockMvc.perform(get("/welcome"))
      .andExpect(status().isOk())
      .andExpect(view().name("bean-name-handler-mapping"));
}

If we explicitly register a different handler mapper, the default mapper will be overridden. However, it is interesting to see what happens when two mappers are explicitly registered:

@Configuration
public class HandlerMappingPrioritiesConfig {

    @Bean
    BeanNameUrlHandlerMapping beanNameUrlHandlerMapping() {
        BeanNameUrlHandlerMapping beanNameUrlHandlerMapping 
          = new BeanNameUrlHandlerMapping();
        return beanNameUrlHandlerMapping;
    }

    @Bean
    public SimpleUrlHandlerMapping simpleUrlHandlerMapping() {
        SimpleUrlHandlerMapping simpleUrlHandlerMapping
          = new SimpleUrlHandlerMapping();
        Map<String, Object> urlMap = new HashMap<>();
        urlMap.put("/welcome", simpleUrlMapping());
        simpleUrlHandlerMapping.setUrlMap(urlMap);
        return simpleUrlHandlerMapping;
    }

    @Bean
    public SimpleUrlMappingController simpleUrlMapping() {
        return new SimpleUrlMappingController();
    }

    @Bean("/welcome")
    public BeanNameHandlerMappingController beanNameHandlerMapping() {
        return new BeanNameHandlerMappingController();
    }
}

To get the control over which mapping is used, the priorities are set using setOrder(int order) method. This method takes one int parameter where lower value mean higher priority.

In XML configuration you can configure priorities by using a property called “order”:

<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
    <property name="order" value="2" />
</bean>

Let us add order properties to handler mapping beans, via following beanNameUrlHandlerMapping.setOrder(1) and simpleUrlHandlerMapping.setOrder(0). The lower value of the order property reflects higher precedence. Let us assert new behaviour with the test:

@Test
public void whenConfiguringPriorities_thenMappedOK() {
    mockMvc.perform(get("/welcome"))
      .andExpect(status().isOk())
      .andExpect(view().name("simple-url-handler-mapping"));
}

When testing the above configuration, you see that requests to “/welcome” will be handled by SimpleUrlHandlerMapping bean which calls a SimpleUrlHandlerController and returns simple-url-handler-mapping view. We can easily configure the BeanNameHandlerMapping to take precedence by adjusting accordingly the values of order property.

6. Conclusion

In this article we discussed how URL mapping are handled in Spring MVC framework by exploring the different implementations in the framework.

The code accompanying this article can be found over on GitHub.

Course – LS – All

Get started with Spring and Spring Boot, through the Learn Spring course:

>> CHECK OUT THE COURSE
res – REST with Spring (eBook) (everywhere)
Comments are closed on this article!