March 29th, 2013

In the Spring MVC framework XML based configuration the mvc namespace provides a number of handy elements for configuration your web application. If you include the following in your XML configuration

<mvc:annotation-driven />

then org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport does a lot of the heavy lifting for you including instantiating a HandlerMapping and injecting interceptors and a ContentNegotiationManager that you can define using the  <mvc:interceptors> element and content-negotiation-manager attribute of <mvc:annotation-driven> respectively.

If like me you want all this work doing for you but you want to tweak a property of the HandlerMapping then you run into a problem. There’s no facility provided by the XML configuration to allow this. This is covered in a Spring issue (go vote for it).

Here I’m sharing the workaround I’ve settled on which, unlike every other suggestion I’ve spotted, maintains support for <mvc:interceptors>.

Override the DispatcherServlet to configure RequestMappingHandlerMapping

This code assumes you’re running in a Servlet 3.0 container. If not you’ll have to configure your DispatcherServlet in web.xml.

Here I’ve used initStrategies (where the HandlerMapping instances are added to the DispatcherServlet) to inspect the Spring context for relevant beans and set the property I wanted (stripping URL path parameters).

import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.handler.AbstractHandlerMapping;
@WebServlet(name="spring-dispatcher", loadOnStartup=1, urlPatterns={"/"},
        initParams={@WebInitParam(name="contextConfigLocation", value="/WEB-INF/spring/spring-dispatcher-servlet.xml")})
public class MyDispatcherServlet extends DispatcherServlet {
    @Override
    protected void initStrategies(ApplicationContext context) {
        super.initStrategies(context);
        for (AbstractHandlerMapping handlerMapping : BeanFactoryUtils.beansOfTypeIncludingAncestors(
                context, AbstractHandlerMapping.class, true, false).values()) {

            handlerMapping.setRemoveSemicolonContent(true);
        }
    }
}