Skip to content

Commit cf39078

Browse files
committed
Backport MVC Java config path-related config options
Issue: SPR-14186
1 parent 3d6b0ca commit cf39078

18 files changed

+657
-20
lines changed

spring-webmvc/src/main/java/org/springframework/web/servlet/config/AnnotationDrivenBeanDefinitionParser.java

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2013 the original author or authors.
2+
* Copyright 2002-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -163,6 +163,8 @@ public BeanDefinition parse(Element element, ParserContext parserContext) {
163163
handlerMappingDef.getPropertyValues().add("removeSemicolonContent", !enableMatrixVariables);
164164
}
165165

166+
configurePathMatchingProperties(handlerMappingDef, element, parserContext);
167+
166168
RuntimeBeanReference conversionService = getConversionService(element, source, parserContext);
167169
RuntimeBeanReference validator = getValidator(element, source, parserContext);
168170
RuntimeBeanReference messageCodesResolver = getMessageCodesResolver(element);
@@ -309,6 +311,40 @@ private RuntimeBeanReference getContentNegotiationManager(Element element, Objec
309311
return contentNegotiationManagerRef;
310312
}
311313

314+
private void configurePathMatchingProperties(RootBeanDefinition handlerMappingDef, Element element,
315+
ParserContext parserContext) {
316+
317+
Element pathMatchingElement = DomUtils.getChildElementByTagName(element, "path-matching");
318+
if (pathMatchingElement != null) {
319+
Object source = parserContext.extractSource(element);
320+
if (pathMatchingElement.hasAttribute("suffix-pattern")) {
321+
Boolean useSuffixPatternMatch = Boolean.valueOf(pathMatchingElement.getAttribute("suffix-pattern"));
322+
handlerMappingDef.getPropertyValues().add("useSuffixPatternMatch", useSuffixPatternMatch);
323+
}
324+
if (pathMatchingElement.hasAttribute("trailing-slash")) {
325+
Boolean useTrailingSlashMatch = Boolean.valueOf(pathMatchingElement.getAttribute("trailing-slash"));
326+
handlerMappingDef.getPropertyValues().add("useTrailingSlashMatch", useTrailingSlashMatch);
327+
}
328+
if (pathMatchingElement.hasAttribute("registered-suffixes-only")) {
329+
Boolean useRegisteredSuffixPatternMatch = Boolean.valueOf(pathMatchingElement.getAttribute("registered-suffixes-only"));
330+
handlerMappingDef.getPropertyValues().add("useRegisteredSuffixPatternMatch", useRegisteredSuffixPatternMatch);
331+
}
332+
RuntimeBeanReference pathHelperRef = null;
333+
if (pathMatchingElement.hasAttribute("path-helper")) {
334+
pathHelperRef = new RuntimeBeanReference(pathMatchingElement.getAttribute("path-helper"));
335+
}
336+
pathHelperRef = MvcNamespaceUtils.registerUrlPathHelper(pathHelperRef, parserContext, source);
337+
handlerMappingDef.getPropertyValues().add("urlPathHelper", pathHelperRef);
338+
339+
RuntimeBeanReference pathMatcherRef = null;
340+
if (pathMatchingElement.hasAttribute("path-matcher")) {
341+
pathMatcherRef = new RuntimeBeanReference(pathMatchingElement.getAttribute("path-matcher"));
342+
}
343+
pathMatcherRef = MvcNamespaceUtils.registerPathMatcher(pathMatcherRef, parserContext, source);
344+
handlerMappingDef.getPropertyValues().add("pathMatcher", pathMatcherRef);
345+
}
346+
}
347+
312348
private Properties getDefaultMediaTypes() {
313349
Properties props = new Properties();
314350
if (romePresent) {

spring-webmvc/src/main/java/org/springframework/web/servlet/config/MvcNamespaceUtils.java

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,12 +17,16 @@
1717
package org.springframework.web.servlet.config;
1818

1919
import org.springframework.beans.factory.config.BeanDefinition;
20+
import org.springframework.beans.factory.config.RuntimeBeanReference;
2021
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
2122
import org.springframework.beans.factory.support.RootBeanDefinition;
2223
import org.springframework.beans.factory.xml.ParserContext;
24+
import org.springframework.util.AntPathMatcher;
25+
import org.springframework.util.PathMatcher;
2326
import org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping;
2427
import org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter;
2528
import org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter;
29+
import org.springframework.web.util.UrlPathHelper;
2630

2731
/**
2832
* Convenience methods for use in MVC namespace BeanDefinitionParsers.
@@ -41,12 +45,70 @@ abstract class MvcNamespaceUtils {
4145
private static final String HTTP_REQUEST_HANDLER_ADAPTER_BEAN_NAME =
4246
HttpRequestHandlerAdapter.class.getName();
4347

48+
private static final String URL_PATH_HELPER_BEAN_NAME = "mvcUrlPathHelper";
49+
50+
private static final String PATH_MATCHER_BEAN_NAME = "mvcPathMatcher";
51+
52+
4453
public static void registerDefaultComponents(ParserContext parserContext, Object source) {
4554
registerBeanNameUrlHandlerMapping(parserContext, source);
4655
registerHttpRequestHandlerAdapter(parserContext, source);
4756
registerSimpleControllerHandlerAdapter(parserContext, source);
4857
}
4958

59+
60+
/**
61+
* Adds an alias to an existing well-known name or registers a new instance of a {@link UrlPathHelper}
62+
* under that well-known name, unless already registered.
63+
* @return a RuntimeBeanReference to this {@link UrlPathHelper} instance
64+
* @since 3.2.17
65+
*/
66+
public static RuntimeBeanReference registerUrlPathHelper(RuntimeBeanReference urlPathHelperRef,
67+
ParserContext parserContext, Object source) {
68+
69+
if (urlPathHelperRef != null) {
70+
if (parserContext.getRegistry().isAlias(URL_PATH_HELPER_BEAN_NAME)) {
71+
parserContext.getRegistry().removeAlias(URL_PATH_HELPER_BEAN_NAME);
72+
}
73+
parserContext.getRegistry().registerAlias(urlPathHelperRef.getBeanName(), URL_PATH_HELPER_BEAN_NAME);
74+
}
75+
else if (!parserContext.getRegistry().isAlias(URL_PATH_HELPER_BEAN_NAME)
76+
&& !parserContext.getRegistry().containsBeanDefinition(URL_PATH_HELPER_BEAN_NAME)) {
77+
RootBeanDefinition urlPathHelperDef = new RootBeanDefinition(UrlPathHelper.class);
78+
urlPathHelperDef.setSource(source);
79+
urlPathHelperDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
80+
parserContext.getRegistry().registerBeanDefinition(URL_PATH_HELPER_BEAN_NAME, urlPathHelperDef);
81+
parserContext.registerComponent(new BeanComponentDefinition(urlPathHelperDef, URL_PATH_HELPER_BEAN_NAME));
82+
}
83+
return new RuntimeBeanReference(URL_PATH_HELPER_BEAN_NAME);
84+
}
85+
86+
/**
87+
* Adds an alias to an existing well-known name or registers a new instance of a {@link PathMatcher}
88+
* under that well-known name, unless already registered.
89+
* @return a RuntimeBeanReference to this {@link PathMatcher} instance
90+
* @since 3.2.17
91+
*/
92+
public static RuntimeBeanReference registerPathMatcher(RuntimeBeanReference pathMatcherRef,
93+
ParserContext parserContext, Object source) {
94+
95+
if (pathMatcherRef != null) {
96+
if (parserContext.getRegistry().isAlias(PATH_MATCHER_BEAN_NAME)) {
97+
parserContext.getRegistry().removeAlias(PATH_MATCHER_BEAN_NAME);
98+
}
99+
parserContext.getRegistry().registerAlias(pathMatcherRef.getBeanName(), PATH_MATCHER_BEAN_NAME);
100+
}
101+
else if (!parserContext.getRegistry().isAlias(PATH_MATCHER_BEAN_NAME)
102+
&& !parserContext.getRegistry().containsBeanDefinition(PATH_MATCHER_BEAN_NAME)) {
103+
RootBeanDefinition pathMatcherDef = new RootBeanDefinition(AntPathMatcher.class);
104+
pathMatcherDef.setSource(source);
105+
pathMatcherDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
106+
parserContext.getRegistry().registerBeanDefinition(PATH_MATCHER_BEAN_NAME, pathMatcherDef);
107+
parserContext.registerComponent(new BeanComponentDefinition(pathMatcherDef, PATH_MATCHER_BEAN_NAME));
108+
}
109+
return new RuntimeBeanReference(PATH_MATCHER_BEAN_NAME);
110+
}
111+
50112
/**
51113
* Registers an {@link HttpRequestHandlerAdapter} under a well-known
52114
* name unless already registered.

spring-webmvc/src/main/java/org/springframework/web/servlet/config/ResourcesBeanDefinitionParser.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2013 the original author or authors.
2+
* Copyright 2002-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -22,6 +22,7 @@
2222
import org.w3c.dom.Element;
2323

2424
import org.springframework.beans.factory.config.BeanDefinition;
25+
import org.springframework.beans.factory.config.RuntimeBeanReference;
2526
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
2627
import org.springframework.beans.factory.support.ManagedList;
2728
import org.springframework.beans.factory.support.ManagedMap;
@@ -62,10 +63,15 @@ public BeanDefinition parse(Element element, ParserContext parserContext) {
6263
}
6364
urlMap.put(resourceRequestPath, resourceHandlerName);
6465

66+
RuntimeBeanReference pathMatcherRef = MvcNamespaceUtils.registerPathMatcher(null, parserContext, source);
67+
RuntimeBeanReference pathHelperRef = MvcNamespaceUtils.registerUrlPathHelper(null, parserContext, source);
68+
6569
RootBeanDefinition handlerMappingDef = new RootBeanDefinition(SimpleUrlHandlerMapping.class);
6670
handlerMappingDef.setSource(source);
6771
handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
6872
handlerMappingDef.getPropertyValues().add("urlMap", urlMap);
73+
handlerMappingDef.getPropertyValues().add("pathMatcher", pathMatcherRef);
74+
handlerMappingDef.getPropertyValues().add("urlPathHelper", pathHelperRef);
6975

7076
String order = element.getAttribute("order");
7177
// use a default of near-lowest precedence, still allowing for even lower precedence in other mappings

spring-webmvc/src/main/java/org/springframework/web/servlet/config/ViewControllerBeanDefinitionParser.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -76,6 +76,10 @@ private BeanDefinition registerHandlerMapping(ParserContext parserContext, Objec
7676
RootBeanDefinition handlerMappingDef = new RootBeanDefinition(SimpleUrlHandlerMapping.class);
7777
handlerMappingDef.setSource(source);
7878
handlerMappingDef.getPropertyValues().add("order", "1");
79+
handlerMappingDef.getPropertyValues().add("pathMatcher",
80+
MvcNamespaceUtils.registerPathMatcher(null, parserContext, source));
81+
handlerMappingDef.getPropertyValues().add("urlPathHelper",
82+
MvcNamespaceUtils.registerUrlPathHelper(null, parserContext, source));
7983
handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
8084
parserContext.getRegistry().registerBeanDefinition(HANDLER_MAPPING_BEAN_NAME, handlerMappingDef);
8185
parserContext.registerComponent(new BeanComponentDefinition(handlerMappingDef, HANDLER_MAPPING_BEAN_NAME));

spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfiguration.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2015 the original author or authors.
2+
* Copyright 2002-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -67,6 +67,11 @@ public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
6767
this.configurers.configureAsyncSupport(configurer);
6868
}
6969

70+
@Override
71+
public void configurePathMatch(PathMatchConfigurer configurer) {
72+
this.configurers.configurePathMatch(configurer);
73+
}
74+
7075
@Override
7176
protected void addViewControllers(ViewControllerRegistry registry) {
7277
this.configurers.addViewControllers(registry);
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/*
2+
* Copyright 2002-2016 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.web.servlet.config.annotation;
18+
19+
import org.springframework.util.PathMatcher;
20+
import org.springframework.web.util.UrlPathHelper;
21+
22+
/**
23+
* Helps with configuring HandlerMappings path matching options such as trailing
24+
* slash match, suffix registration, path matcher and path helper.
25+
*
26+
* <p>Configured path matcher and path helper instances are shared for:
27+
* <ul>
28+
* <li>RequestMappings</li>
29+
* <li>ViewControllerMappings</li>
30+
* <li>ResourcesMappings</li>
31+
* </ul>
32+
*
33+
* @author Brian Clozel
34+
* @since 3.2.17
35+
* @see org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
36+
* @see org.springframework.web.servlet.handler.SimpleUrlHandlerMapping
37+
*/
38+
public class PathMatchConfigurer {
39+
40+
private Boolean suffixPatternMatch;
41+
42+
private Boolean trailingSlashMatch;
43+
44+
private Boolean registeredSuffixPatternMatch;
45+
46+
private UrlPathHelper urlPathHelper;
47+
48+
private PathMatcher pathMatcher;
49+
50+
51+
/**
52+
* Whether to use suffix pattern match (".*") when matching patterns to
53+
* requests. If enabled a method mapped to "/users" also matches to "/users.*".
54+
* <p>By default this is set to {@code true}.
55+
* @see #registeredSuffixPatternMatch
56+
*/
57+
public PathMatchConfigurer setUseSuffixPatternMatch(Boolean suffixPatternMatch) {
58+
this.suffixPatternMatch = suffixPatternMatch;
59+
return this;
60+
}
61+
62+
/**
63+
* Whether to match to URLs irrespective of the presence of a trailing slash.
64+
* If enabled a method mapped to "/users" also matches to "/users/".
65+
* <p>The default value is {@code true}.
66+
*/
67+
public PathMatchConfigurer setUseTrailingSlashMatch(Boolean trailingSlashMatch) {
68+
this.trailingSlashMatch = trailingSlashMatch;
69+
return this;
70+
}
71+
72+
/**
73+
* Whether suffix pattern matching should work only against path extensions
74+
* explicitly registered when you
75+
* {@link WebMvcConfigurer#configureContentNegotiation configure content
76+
* negotiation}. This is generally recommended to reduce ambiguity and to
77+
* avoid issues such as when a "." appears in the path for other reasons.
78+
* <p>By default this is set to "false".
79+
* @see WebMvcConfigurer#configureContentNegotiation
80+
*/
81+
public PathMatchConfigurer setUseRegisteredSuffixPatternMatch(
82+
Boolean registeredSuffixPatternMatch) {
83+
84+
this.registeredSuffixPatternMatch = registeredSuffixPatternMatch;
85+
return this;
86+
}
87+
88+
/**
89+
* Set the UrlPathHelper to use for resolution of lookup paths.
90+
* <p>Use this to override the default UrlPathHelper with a custom subclass,
91+
* or to share common UrlPathHelper settings across multiple HandlerMappings
92+
* and MethodNameResolvers.
93+
*/
94+
public PathMatchConfigurer setUrlPathHelper(UrlPathHelper urlPathHelper) {
95+
this.urlPathHelper = urlPathHelper;
96+
return this;
97+
}
98+
99+
/**
100+
* Set the PathMatcher implementation to use for matching URL paths
101+
* against registered URL patterns. Default is AntPathMatcher.
102+
* @see org.springframework.util.AntPathMatcher
103+
*/
104+
public PathMatchConfigurer setPathMatcher(PathMatcher pathMatcher) {
105+
this.pathMatcher = pathMatcher;
106+
return this;
107+
}
108+
109+
public Boolean isUseSuffixPatternMatch() {
110+
return this.suffixPatternMatch;
111+
}
112+
113+
public Boolean isUseTrailingSlashMatch() {
114+
return this.trailingSlashMatch;
115+
}
116+
117+
public Boolean isUseRegisteredSuffixPatternMatch() {
118+
return this.registeredSuffixPatternMatch;
119+
}
120+
121+
public UrlPathHelper getUrlPathHelper() {
122+
return this.urlPathHelper;
123+
}
124+
125+
public PathMatcher getPathMatcher() {
126+
return this.pathMatcher;
127+
}
128+
129+
}

0 commit comments

Comments
 (0)