diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/AnnotationDrivenBeanDefinitionParser.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/AnnotationDrivenBeanDefinitionParser.java
index ae22662d626..cec5b1d60ca 100644
--- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/AnnotationDrivenBeanDefinitionParser.java
+++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/AnnotationDrivenBeanDefinitionParser.java
@@ -77,7 +77,7 @@ import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolv
/**
* A {@link BeanDefinitionParser} that provides the configuration for the
- * {@code
This class registers the following {@link HandlerMapping}s:
*This class registers an {@link org.springframework.util.AntPathMatcher} + * and a {@link org.springframework.web.util.UrlPathHelper} to be used by: + *
Both the {@link RequestMappingHandlerAdapter} and the * {@link ExceptionHandlerExceptionResolver} are configured with instances of @@ -174,7 +184,7 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser { handlerMappingDef.getPropertyValues().add("removeSemicolonContent", !enableMatrixVariables); } - configurePathMatchingProperties(handlerMappingDef, element); + configurePathMatchingProperties(handlerMappingDef, element, parserContext); RuntimeBeanReference conversionService = getConversionService(element, source, parserContext); RuntimeBeanReference validator = getValidator(element, source, parserContext); @@ -348,9 +358,11 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser { return contentNegotiationManagerRef; } - private void configurePathMatchingProperties(RootBeanDefinition handlerMappingDef, Element element) { + private void configurePathMatchingProperties(RootBeanDefinition handlerMappingDef, + Element element, ParserContext parserContext) { Element pathMatchingElement = DomUtils.getChildElementByTagName(element, "path-matching"); if(pathMatchingElement != null) { + Object source = parserContext.extractSource(element); if (pathMatchingElement.hasAttribute("suffix-pattern")) { Boolean useSuffixPatternMatch = Boolean.valueOf(pathMatchingElement.getAttribute("suffix-pattern")); handlerMappingDef.getPropertyValues().add("useSuffixPatternMatch", useSuffixPatternMatch); @@ -363,14 +375,19 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser { Boolean useRegisteredSuffixPatternMatch = Boolean.valueOf(pathMatchingElement.getAttribute("registered-suffixes-only")); handlerMappingDef.getPropertyValues().add("useRegisteredSuffixPatternMatch", useRegisteredSuffixPatternMatch); } + RuntimeBeanReference pathHelperRef = null; if (pathMatchingElement.hasAttribute("path-helper")) { - RuntimeBeanReference pathHelperRef = new RuntimeBeanReference(pathMatchingElement.getAttribute("path-helper")); - handlerMappingDef.getPropertyValues().add("urlPathHelper", pathHelperRef); + pathHelperRef = new RuntimeBeanReference(pathMatchingElement.getAttribute("path-helper")); } + pathHelperRef = MvcNamespaceUtils.registerUrlPathHelper(pathHelperRef, parserContext, source); + handlerMappingDef.getPropertyValues().add("urlPathHelper", pathHelperRef); + + RuntimeBeanReference pathMatcherRef = null; if (pathMatchingElement.hasAttribute("path-matcher")) { - RuntimeBeanReference pathMatcherRef = new RuntimeBeanReference(pathMatchingElement.getAttribute("path-matcher")); - handlerMappingDef.getPropertyValues().add("pathMatcher", pathMatcherRef); + pathMatcherRef = new RuntimeBeanReference(pathMatchingElement.getAttribute("path-matcher")); } + pathMatcherRef = MvcNamespaceUtils.registerPathMatcher(pathMatcherRef, parserContext, source); + handlerMappingDef.getPropertyValues().add("pathMatcher", pathMatcherRef); } } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/MvcNamespaceUtils.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/MvcNamespaceUtils.java index abf53b4e2c8..e6c426c1cff 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/MvcNamespaceUtils.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/MvcNamespaceUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,17 +17,22 @@ package org.springframework.web.servlet.config; import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.RuntimeBeanReference; import org.springframework.beans.factory.parsing.BeanComponentDefinition; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.util.AntPathMatcher; +import org.springframework.util.PathMatcher; import org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping; import org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter; import org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter; +import org.springframework.web.util.UrlPathHelper; /** * Convenience methods for use in MVC namespace BeanDefinitionParsers. * * @author Rossen Stoyanchev + * @author Brian Clozel * @since 3.1 */ abstract class MvcNamespaceUtils { @@ -41,12 +46,62 @@ abstract class MvcNamespaceUtils { private static final String HTTP_REQUEST_HANDLER_ADAPTER_BEAN_NAME = HttpRequestHandlerAdapter.class.getName(); + private static final String URL_PATH_HELPER_BEAN_NAME = "mvcUrlPathHelper"; + + private static final String PATH_MATCHER_BEAN_NAME = "mvcPathMatcher"; + public static void registerDefaultComponents(ParserContext parserContext, Object source) { registerBeanNameUrlHandlerMapping(parserContext, source); registerHttpRequestHandlerAdapter(parserContext, source); registerSimpleControllerHandlerAdapter(parserContext, source); } + /** + * Adds an alias to an existing well-known name or registers a new instance of a {@link UrlPathHelper} + * under that well-known name, unless already registered. + * @return a RuntimeBeanReference to this {@link UrlPathHelper} instance + */ + public static RuntimeBeanReference registerUrlPathHelper(RuntimeBeanReference urlPathHelperRef, ParserContext parserContext, Object source) { + if(urlPathHelperRef != null) { + if(parserContext.getRegistry().isAlias(URL_PATH_HELPER_BEAN_NAME)) { + parserContext.getRegistry().removeAlias(URL_PATH_HELPER_BEAN_NAME); + } + parserContext.getRegistry().registerAlias(urlPathHelperRef.getBeanName(), URL_PATH_HELPER_BEAN_NAME); + } + else if (!parserContext.getRegistry().isAlias(URL_PATH_HELPER_BEAN_NAME) + && !parserContext.getRegistry().containsBeanDefinition(URL_PATH_HELPER_BEAN_NAME)) { + RootBeanDefinition urlPathHelperDef = new RootBeanDefinition(UrlPathHelper.class); + urlPathHelperDef.setSource(source); + urlPathHelperDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); + parserContext.getRegistry().registerBeanDefinition(URL_PATH_HELPER_BEAN_NAME, urlPathHelperDef); + parserContext.registerComponent(new BeanComponentDefinition(urlPathHelperDef, URL_PATH_HELPER_BEAN_NAME)); + } + return new RuntimeBeanReference(URL_PATH_HELPER_BEAN_NAME); + } + + /** + * Adds an alias to an existing well-known name or registers a new instance of a {@link PathMatcher} + * under that well-known name, unless already registered. + * @return a RuntimeBeanReference to this {@link PathMatcher} instance + */ + public static RuntimeBeanReference registerPathMatcher(RuntimeBeanReference pathMatcherRef, ParserContext parserContext, Object source) { + if(pathMatcherRef != null) { + if(parserContext.getRegistry().isAlias(PATH_MATCHER_BEAN_NAME)) { + parserContext.getRegistry().removeAlias(PATH_MATCHER_BEAN_NAME); + } + parserContext.getRegistry().registerAlias(pathMatcherRef.getBeanName(), PATH_MATCHER_BEAN_NAME); + } + else if (!parserContext.getRegistry().isAlias(PATH_MATCHER_BEAN_NAME) + && !parserContext.getRegistry().containsBeanDefinition(PATH_MATCHER_BEAN_NAME)) { + RootBeanDefinition pathMatcherDef = new RootBeanDefinition(AntPathMatcher.class); + pathMatcherDef.setSource(source); + pathMatcherDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); + parserContext.getRegistry().registerBeanDefinition(PATH_MATCHER_BEAN_NAME, pathMatcherDef); + parserContext.registerComponent(new BeanComponentDefinition(pathMatcherDef, PATH_MATCHER_BEAN_NAME)); + } + return new RuntimeBeanReference(PATH_MATCHER_BEAN_NAME); + } + /** * Registers an {@link HttpRequestHandlerAdapter} under a well-known * name unless already registered. diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/ResourcesBeanDefinitionParser.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/ResourcesBeanDefinitionParser.java index f08d75e2b8f..49d7ff0ff5e 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/ResourcesBeanDefinitionParser.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/ResourcesBeanDefinitionParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +22,7 @@ import java.util.Map; import org.w3c.dom.Element; import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.RuntimeBeanReference; import org.springframework.beans.factory.parsing.BeanComponentDefinition; import org.springframework.beans.factory.support.ManagedList; import org.springframework.beans.factory.support.ManagedMap; @@ -63,10 +64,14 @@ class ResourcesBeanDefinitionParser implements BeanDefinitionParser { } urlMap.put(resourceRequestPath, resourceHandlerName); + RuntimeBeanReference pathMatcherRef = MvcNamespaceUtils.registerPathMatcher(null, parserContext, source); + RuntimeBeanReference pathHelperRef = MvcNamespaceUtils.registerUrlPathHelper(null, parserContext, source); + RootBeanDefinition handlerMappingDef = new RootBeanDefinition(SimpleUrlHandlerMapping.class); handlerMappingDef.setSource(source); handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); handlerMappingDef.getPropertyValues().add("urlMap", urlMap); + handlerMappingDef.getPropertyValues().add("pathMatcher", pathMatcherRef).add("urlPathHelper", pathHelperRef); String order = element.getAttribute("order"); // use a default of near-lowest precedence, still allowing for even lower precedence in other mappings diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/ViewControllerBeanDefinitionParser.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/ViewControllerBeanDefinitionParser.java index 6ab039507cc..52181a2875f 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/ViewControllerBeanDefinitionParser.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/ViewControllerBeanDefinitionParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ package org.springframework.web.servlet.config; import java.util.Map; import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.RuntimeBeanReference; import org.springframework.beans.factory.parsing.BeanComponentDefinition; import org.springframework.beans.factory.support.ManagedMap; import org.springframework.beans.factory.support.RootBeanDefinition; @@ -75,9 +76,13 @@ class ViewControllerBeanDefinitionParser implements BeanDefinitionParser { private BeanDefinition registerHandlerMapping(ParserContext parserContext, Object source) { if (!parserContext.getRegistry().containsBeanDefinition(HANDLER_MAPPING_BEAN_NAME)) { + RuntimeBeanReference pathMatcherRef = MvcNamespaceUtils.registerPathMatcher(null, parserContext, source); + RuntimeBeanReference pathHelperRef = MvcNamespaceUtils.registerUrlPathHelper(null, parserContext, source); + RootBeanDefinition handlerMappingDef = new RootBeanDefinition(SimpleUrlHandlerMapping.class); handlerMappingDef.setSource(source); handlerMappingDef.getPropertyValues().add("order", "1"); + handlerMappingDef.getPropertyValues().add("pathMatcher", pathMatcherRef).add("urlPathHelper", pathHelperRef); handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); parserContext.getRegistry().registerBeanDefinition(HANDLER_MAPPING_BEAN_NAME, handlerMappingDef); parserContext.registerComponent(new BeanComponentDefinition(handlerMappingDef, HANDLER_MAPPING_BEAN_NAME)); diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/PathMatchConfigurer.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/PathMatchConfigurer.java index 119c0d8fa31..dbede71a53a 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/PathMatchConfigurer.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/PathMatchConfigurer.java @@ -20,12 +20,19 @@ import org.springframework.util.PathMatcher; import org.springframework.web.util.UrlPathHelper; /** - * Helps with configuring {@link org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping} - * path matching options such as trailing slash match, suffix registration or path matcher/helper. + * Helps with configuring HandlerMappings path matching options such as trailing slash match, + * suffix registration, path matcher and path helper. + * Configured path matcher and path helper instances are shared for: + *
Registers an {@link AntPathMatcher} and a {@link UrlPathHelper} + * to be used by: + *
Both the {@link RequestMappingHandlerAdapter} and the * {@link ExceptionHandlerExceptionResolver} are configured with default * instances of the following by default: @@ -145,6 +155,7 @@ import org.springframework.web.util.UrlPathHelper; * @see WebMvcConfigurerAdapter * * @author Rossen Stoyanchev + * @author Brian Clozel * @since 3.1 */ public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware { @@ -318,6 +329,8 @@ public class WebMvcConfigurationSupport implements ApplicationContextAware, Serv AbstractHandlerMapping handlerMapping = registry.getHandlerMapping(); handlerMapping = handlerMapping != null ? handlerMapping : new EmptyHandlerMapping(); + handlerMapping.setPathMatcher(mvcPathMatcher()); + handlerMapping.setUrlPathHelper(mvcUrlPathHelper()); handlerMapping.setInterceptors(getInterceptors()); return handlerMapping; } @@ -353,6 +366,8 @@ public class WebMvcConfigurationSupport implements ApplicationContextAware, Serv addResourceHandlers(registry); AbstractHandlerMapping handlerMapping = registry.getHandlerMapping(); handlerMapping = handlerMapping != null ? handlerMapping : new EmptyHandlerMapping(); + handlerMapping.setPathMatcher(mvcPathMatcher()); + handlerMapping.setUrlPathHelper(mvcUrlPathHelper()); return handlerMapping; } @@ -509,6 +524,40 @@ public class WebMvcConfigurationSupport implements ApplicationContextAware, Serv return validator; } + /** + * Return a global {@link PathMatcher} instance for path matching + * patterns in {@link HandlerMapping}s. + * This instance can be configured using the {@link PathMatchConfigurer} + * in {@link #configurePathMatch(PathMatchConfigurer)}. + * @since 4.1 + */ + @Bean + public PathMatcher mvcPathMatcher() { + if(getPathMatchConfigurer().getPathMatcher() != null) { + return getPathMatchConfigurer().getPathMatcher(); + } + else { + return new AntPathMatcher(); + } + } + + /** + * Return a global {@link UrlPathHelper} instance for path matching + * patterns in {@link HandlerMapping}s. + * This instance can be configured using the {@link PathMatchConfigurer} + * in {@link #configurePathMatch(PathMatchConfigurer)}. + * @since 4.1 + */ + @Bean + public UrlPathHelper mvcUrlPathHelper() { + if(getPathMatchConfigurer().getUrlPathHelper() != null) { + return getPathMatchConfigurer().getUrlPathHelper(); + } + else { + return new UrlPathHelper(); + } + } + /** * Override this method to provide a custom {@link Validator}. */ diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurer.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurer.java index a65b6326e8c..6af00bf29b9 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurer.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurer.java @@ -80,7 +80,14 @@ public interface WebMvcConfigurer { void configureAsyncSupport(AsyncSupportConfigurer configurer); /** - * Configure path matching options. + * Helps with configuring HandlerMappings path matching options such as trailing slash match, + * suffix registration, path matcher and path helper. + * Configured path matcher and path helper instances are shared for: + *