Friday, January 4, 2013

Spring MVC - Customizing RequestMappingHandlerMapping

When Spring MVC is configured using <mvc:annotation-driven/> in an xml bean definition file, internally a component called RequestMappingHandlerMapping gets registered with Spring MVC. This component or in general a HandlerMapping component is responsible for routing request URI's to handlers which are the controller methods annotated with @RequestMapping annotation.

There are two specific configurations in RequestMappingHandlerMapping that may be non-intuitive:

The first configuration is "useSuffixPatternMatch", which is if say a uri "/members" maps to method to return a list of entities, "/members.xyz" would map to the same handler method.

The second configuration is "useTrailingSlashMatch" which is that "/members" and "/members/" would map to the same handler method.

If these two behavior need to be modified, the way to do that is to configure the RequestMappingHandlerMapping.

If <mvc:annotation-driven/> has been used to define the RequestMappingHandlerMapping, then the unwieldy way to go about it would be to remove <mvc:annotation-driven/> and instead to configure Spring MVC by expanding out the components that get registered by the custom namespace handler handling the "mvc" namespace, which is something along these lines(this is not complete and is being shown just to demonstrate the complexity of the configuration):

<bean name="handlerAdapter" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
 <property name="webBindingInitializer">
  <bean class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
   <property name="conversionService" ref="conversionService"></property>
   <property name="validator">
    <bean class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
   </property>
  </bean>
 </property>
 <property name="messageConverters">
  <list>
   <bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter"/>
   <bean class="org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter"/>
   <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
   <bean class="org.springframework.http.converter.StringHttpMessageConverter"></bean>
   <bean class="org.springframework.http.converter.ResourceHttpMessageConverter"></bean>
   <bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter"></bean>
   <bean class="org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter"></bean>
  </list>
 </property>
</bean>

<bean name="handlerMapping" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
 <property name="useTrailingSlashMatch" value="false"></property>
</bean> 

This is definitely not a good way to go about changing the configuration. Instead if faced with the need to configure RequestMappingHandlerMapping a far better approach is to move a part or the entire web-mvc configuration to Java @Configuration this way:

package mvcsample.spring;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;



@Configuration
public class WebConfig extends WebMvcConfigurationSupport{
 @Bean
 public RequestMappingHandlerMapping requestMappingHandlerMapping() {
  RequestMappingHandlerMapping handlerMapping = super.requestMappingHandlerMapping();
  handlerMapping.setUseSuffixPatternMatch(false);
  handlerMapping.setUseTrailingSlashMatch(false);
  return handlerMapping;
 } 
}

and to import this configuration into the rest of the configuration xml(there are other documented ways also):

<bean class="mvcsample.spring.WebConfig"/>

<!-- 
<mvc:annotation-driven>
</mvc:annotation-driven> 
-->

@Configuration thus provides a simpler mechanism to configure the components in Spring MVC and migration from xml based configuration to Java configuration is highly recommended to simplify the configuration and manage customizations.

4 comments:

  1. I'm curious - why do you mix and match annotation-based configuration with the XML one? Wouldn't it make a bit more sense to use one or the other?

    ReplyDelete
  2. Could you please share source code for this example?

    ReplyDelete