Skip to content

Commit 92cf1e1

Browse files
committed
Enable use of parsed patterns by default in Spring MVC
Closes spring-projectsgh-28607
1 parent 8a9b082 commit 92cf1e1

File tree

16 files changed

+234
-146
lines changed

16 files changed

+234
-146
lines changed

spring-test/src/main/java/org/springframework/test/web/servlet/setup/StandaloneMockMvcBuilder.java

+12-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2021 the original author or authors.
2+
* Copyright 2002-2022 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.
@@ -127,6 +127,8 @@ public class StandaloneMockMvcBuilder extends AbstractMockMvcBuilder<StandaloneM
127127
@Nullable
128128
private FlashMapManager flashMapManager;
129129

130+
private boolean preferPathMatcher = false;
131+
130132
@Nullable
131133
private PathPatternParser patternParser;
132134

@@ -317,8 +319,10 @@ public StandaloneMockMvcBuilder setFlashMapManager(FlashMapManager flashMapManag
317319
* @param parser the parser to use
318320
* @since 5.3
319321
*/
320-
public void setPatternParser(PathPatternParser parser) {
322+
public StandaloneMockMvcBuilder setPatternParser(@Nullable PathPatternParser parser) {
321323
this.patternParser = parser;
324+
this.preferPathMatcher = (this.patternParser == null);
325+
return this;
322326
}
323327

324328
/**
@@ -332,6 +336,7 @@ public void setPatternParser(PathPatternParser parser) {
332336
@Deprecated
333337
public StandaloneMockMvcBuilder setUseSuffixPatternMatch(boolean useSuffixPatternMatch) {
334338
this.useSuffixPatternMatch = useSuffixPatternMatch;
339+
this.preferPathMatcher |= useSuffixPatternMatch;
335340
return this;
336341
}
337342

@@ -468,15 +473,16 @@ public RequestMappingHandlerMapping getHandlerMapping(
468473

469474
RequestMappingHandlerMapping handlerMapping = handlerMappingFactory.get();
470475
handlerMapping.setEmbeddedValueResolver(new StaticStringValueResolver(placeholderValues));
471-
if (patternParser != null) {
472-
handlerMapping.setPatternParser(patternParser);
473-
}
474-
else {
476+
if (patternParser == null && preferPathMatcher) {
477+
handlerMapping.setPatternParser(null);
475478
handlerMapping.setUseSuffixPatternMatch(useSuffixPatternMatch);
476479
if (removeSemicolonContent != null) {
477480
handlerMapping.setRemoveSemicolonContent(removeSemicolonContent);
478481
}
479482
}
483+
else if (patternParser != null) {
484+
handlerMapping.setPatternParser(patternParser);
485+
}
480486
handlerMapping.setUseTrailingSlashMatch(useTrailingSlashPatternMatch);
481487
handlerMapping.setOrder(0);
482488
handlerMapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider));

spring-web/src/main/java/org/springframework/web/cors/UrlBasedCorsConfigurationSource.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 the original author or authors.
2+
* Copyright 2002-2022 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.
@@ -53,7 +53,7 @@
5353
*/
5454
public class UrlBasedCorsConfigurationSource implements CorsConfigurationSource {
5555

56-
private static PathMatcher defaultPathMatcher = new AntPathMatcher();
56+
private static final PathMatcher defaultPathMatcher = new AntPathMatcher();
5757

5858

5959
private final PathPatternParser patternParser;
@@ -157,7 +157,7 @@ public void setUrlPathHelper(UrlPathHelper urlPathHelper) {
157157
* pattern matching with {@link PathMatcher} or with parsed {@link PathPattern}s.
158158
* <p>In Spring MVC, either a resolved String lookupPath or a parsed
159159
* {@code RequestPath} is always available within {@code DispatcherServlet}
160-
* processing. However in a Servlet {@code Filter} such as {@code CorsFilter}
160+
* processing. However, in a Servlet {@code Filter} such as {@code CorsFilter}
161161
* that may or may not be the case.
162162
* <p>By default this is set to {@code true} in which case lazy lookupPath
163163
* initialization is allowed. Set this to {@code false} when an

spring-web/src/main/java/org/springframework/web/util/ServletRequestPathUtils.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,9 @@ public static RequestPath parse(HttpServletRequest request) {
252252
if (UrlPathHelper.servlet4Present) {
253253
String servletPathPrefix = Servlet4Delegate.getServletPathPrefix(request);
254254
if (StringUtils.hasText(servletPathPrefix)) {
255+
if (servletPathPrefix.endsWith("/")) {
256+
servletPathPrefix = servletPathPrefix.substring(0, servletPathPrefix.length() - 1);
257+
}
255258
return new ServletRequestPath(requestUri, request.getContextPath(), servletPathPrefix);
256259
}
257260
}
@@ -272,8 +275,7 @@ public static String getServletPathPrefix(HttpServletRequest request) {
272275
if (mapping == null) {
273276
mapping = request.getHttpServletMapping();
274277
}
275-
MappingMatch match = mapping.getMappingMatch();
276-
if (!ObjectUtils.nullSafeEquals(match, MappingMatch.PATH)) {
278+
if (!ObjectUtils.nullSafeEquals(mapping.getMappingMatch(), MappingMatch.PATH)) {
277279
return null;
278280
}
279281
String servletPath = (String) request.getAttribute(WebUtils.INCLUDE_SERVLET_PATH_ATTRIBUTE);

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

+19-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2021 the original author or authors.
2+
* Copyright 2002-2022 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.
@@ -405,33 +405,45 @@ private void configurePathMatchingProperties(
405405
if (pathMatchingElement != null) {
406406
Object source = context.extractSource(element);
407407

408-
if (pathMatchingElement.hasAttribute("suffix-pattern")) {
409-
Boolean useSuffixPatternMatch = Boolean.valueOf(pathMatchingElement.getAttribute("suffix-pattern"));
410-
handlerMappingDef.getPropertyValues().add("useSuffixPatternMatch", useSuffixPatternMatch);
411-
}
412408
if (pathMatchingElement.hasAttribute("trailing-slash")) {
413-
Boolean useTrailingSlashMatch = Boolean.valueOf(pathMatchingElement.getAttribute("trailing-slash"));
409+
boolean useTrailingSlashMatch = Boolean.parseBoolean(pathMatchingElement.getAttribute("trailing-slash"));
414410
handlerMappingDef.getPropertyValues().add("useTrailingSlashMatch", useTrailingSlashMatch);
415411
}
412+
413+
boolean preferPathMatcher = false;
414+
415+
if (pathMatchingElement.hasAttribute("suffix-pattern")) {
416+
boolean useSuffixPatternMatch = Boolean.parseBoolean(pathMatchingElement.getAttribute("suffix-pattern"));
417+
handlerMappingDef.getPropertyValues().add("useSuffixPatternMatch", useSuffixPatternMatch);
418+
preferPathMatcher |= useSuffixPatternMatch;
419+
}
416420
if (pathMatchingElement.hasAttribute("registered-suffixes-only")) {
417-
Boolean useRegisteredSuffixPatternMatch = Boolean.valueOf(pathMatchingElement.getAttribute("registered-suffixes-only"));
421+
boolean useRegisteredSuffixPatternMatch = Boolean.parseBoolean(pathMatchingElement.getAttribute("registered-suffixes-only"));
418422
handlerMappingDef.getPropertyValues().add("useRegisteredSuffixPatternMatch", useRegisteredSuffixPatternMatch);
423+
preferPathMatcher |= useRegisteredSuffixPatternMatch;
419424
}
420425

421426
RuntimeBeanReference pathHelperRef = null;
422427
if (pathMatchingElement.hasAttribute("path-helper")) {
423428
pathHelperRef = new RuntimeBeanReference(pathMatchingElement.getAttribute("path-helper"));
429+
preferPathMatcher = true;
424430
}
425431
pathHelperRef = MvcNamespaceUtils.registerUrlPathHelper(pathHelperRef, context, source);
426432
handlerMappingDef.getPropertyValues().add("urlPathHelper", pathHelperRef);
427433

428434
RuntimeBeanReference pathMatcherRef = null;
429435
if (pathMatchingElement.hasAttribute("path-matcher")) {
430436
pathMatcherRef = new RuntimeBeanReference(pathMatchingElement.getAttribute("path-matcher"));
437+
preferPathMatcher = true;
431438
}
432439
pathMatcherRef = MvcNamespaceUtils.registerPathMatcher(pathMatcherRef, context, source);
433440
handlerMappingDef.getPropertyValues().add("pathMatcher", pathMatcherRef);
441+
442+
if (preferPathMatcher) {
443+
handlerMappingDef.getPropertyValues().add("patternParser", null);
444+
}
434445
}
446+
435447
}
436448

437449
private Properties getDefaultMediaTypes() {

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

+61-18
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2021 the original author or authors.
2+
* Copyright 2002-2022 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.
@@ -23,7 +23,6 @@
2323
import org.springframework.lang.Nullable;
2424
import org.springframework.util.AntPathMatcher;
2525
import org.springframework.util.PathMatcher;
26-
import org.springframework.web.servlet.handler.AbstractHandlerMapping;
2726
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
2827
import org.springframework.web.util.UrlPathHelper;
2928
import org.springframework.web.util.pattern.PathPattern;
@@ -34,14 +33,19 @@
3433
* <ul>
3534
* <li>{@link WebMvcConfigurationSupport#requestMappingHandlerMapping}</li>
3635
* <li>{@link WebMvcConfigurationSupport#viewControllerHandlerMapping}</li>
36+
* <li>{@link WebMvcConfigurationSupport#beanNameHandlerMapping}</li>
37+
* <li>{@link WebMvcConfigurationSupport#routerFunctionMapping}</li>
3738
* <li>{@link WebMvcConfigurationSupport#resourceHandlerMapping}</li>
3839
* </ul>
3940
*
4041
* @author Brian Clozel
42+
* @author Rossen Stoyanchev
4143
* @since 4.0.3
4244
*/
4345
public class PathMatchConfigurer {
4446

47+
private boolean preferPathMatcher = false;
48+
4549
@Nullable
4650
private PathPatternParser patternParser;
4751

@@ -74,17 +78,28 @@ public class PathMatchConfigurer {
7478

7579

7680
/**
77-
* Enable use of parsed {@link PathPattern}s as described in
78-
* {@link AbstractHandlerMapping#setPatternParser(PathPatternParser)}.
79-
* <p><strong>Note:</strong> This is mutually exclusive with use of
80-
* {@link #setUrlPathHelper(UrlPathHelper)} and
81-
* {@link #setPathMatcher(PathMatcher)}.
82-
* <p>By default this is not enabled.
81+
* Set the {@link PathPatternParser} to parse {@link PathPattern patterns}
82+
* with for URL path matching. Parsed patterns provide a more modern and
83+
* efficient alternative to String path matching via {@link AntPathMatcher}.
84+
* <p><strong>Note:</strong> This property is mutually exclusive with the
85+
* following other, {@code AntPathMatcher} related properties:
86+
* <ul>
87+
* <li>{@link #setUseSuffixPatternMatch(Boolean)}
88+
* <li>{@link #setUseRegisteredSuffixPatternMatch(Boolean)}
89+
* <li>{@link #setUrlPathHelper(UrlPathHelper)}
90+
* <li>{@link #setPathMatcher(PathMatcher)}
91+
* </ul>
92+
* <p>By default, as of 6.0, a {@link PathPatternParser} with default
93+
* settings is used, which enables parsed {@link PathPattern patterns}.
94+
* Set this property to {@code null} to fall back on String path matching via
95+
* {@link AntPathMatcher} instead, or alternatively, setting one of the above
96+
* listed {@code AntPathMatcher} related properties has the same effect.
8397
* @param patternParser the parser to pre-parse patterns with
8498
* @since 5.3
8599
*/
86-
public PathMatchConfigurer setPatternParser(PathPatternParser patternParser) {
100+
public PathMatchConfigurer setPatternParser(@Nullable PathPatternParser patternParser) {
87101
this.patternParser = patternParser;
102+
this.preferPathMatcher = (patternParser == null);
88103
return this;
89104
}
90105

@@ -120,9 +135,11 @@ public PathMatchConfigurer addPathPrefix(String prefix, Predicate<Class<?>> pred
120135
/**
121136
* Whether to use suffix pattern match (".*") when matching patterns to
122137
* requests. If enabled a method mapped to "/users" also matches to "/users.*".
138+
* <p><strong>Note:</strong> This property is mutually exclusive with
139+
* {@link #setPatternParser(PathPatternParser)}. If set, it enables use of
140+
* String path matching, unless a {@code PathPatternParser} is also
141+
* explicitly set in which case this property is ignored.
123142
* <p>By default this is set to {@code false}.
124-
* <p><strong>Note:</strong> This property is mutually exclusive with and
125-
* ignored when {@link #setPatternParser(PathPatternParser)} is set.
126143
* @deprecated as of 5.2.4. See class-level note in
127144
* {@link RequestMappingHandlerMapping} on the deprecation of path extension
128145
* config options. As there is no replacement for this method, in 5.2.x it is
@@ -132,6 +149,7 @@ public PathMatchConfigurer addPathPrefix(String prefix, Predicate<Class<?>> pred
132149
@Deprecated
133150
public PathMatchConfigurer setUseSuffixPatternMatch(Boolean suffixPatternMatch) {
134151
this.suffixPatternMatch = suffixPatternMatch;
152+
this.preferPathMatcher |= suffixPatternMatch;
135153
return this;
136154
}
137155

@@ -141,41 +159,66 @@ public PathMatchConfigurer setUseSuffixPatternMatch(Boolean suffixPatternMatch)
141159
* {@link WebMvcConfigurer#configureContentNegotiation configure content
142160
* negotiation}. This is generally recommended to reduce ambiguity and to
143161
* avoid issues such as when a "." appears in the path for other reasons.
162+
* <p><strong>Note:</strong> This property is mutually exclusive with
163+
* {@link #setPatternParser(PathPatternParser)}. If set, it enables use of
164+
* String path matching, unless a {@code PathPatternParser} is also
165+
* explicitly set in which case this property is ignored.
144166
* <p>By default this is set to "false".
145-
* <p><strong>Note:</strong> This property is mutually exclusive with and
146-
* ignored when {@link #setPatternParser(PathPatternParser)} is set.
147167
* @deprecated as of 5.2.4. See class-level note in
148168
* {@link RequestMappingHandlerMapping} on the deprecation of path extension
149169
* config options.
150170
*/
151171
@Deprecated
152172
public PathMatchConfigurer setUseRegisteredSuffixPatternMatch(Boolean registeredSuffixPatternMatch) {
153173
this.registeredSuffixPatternMatch = registeredSuffixPatternMatch;
174+
this.preferPathMatcher |= registeredSuffixPatternMatch;
154175
return this;
155176
}
156177

157178
/**
158179
* Set the UrlPathHelper to use to resolve the mapping path for the application.
159-
* <p><strong>Note:</strong> This property is mutually exclusive with and
160-
* ignored when {@link #setPatternParser(PathPatternParser)} is set.
180+
* <p><strong>Note:</strong> This property is mutually exclusive with
181+
* {@link #setPatternParser(PathPatternParser)}. If set, it enables use of
182+
* String path matching, unless a {@code PathPatternParser} is also
183+
* explicitly set in which case this property is ignored.
184+
* <p>By default this is an instance of {@link UrlPathHelper} with default
185+
* settings.
161186
*/
162187
public PathMatchConfigurer setUrlPathHelper(UrlPathHelper urlPathHelper) {
163188
this.urlPathHelper = urlPathHelper;
189+
this.preferPathMatcher = true;
164190
return this;
165191
}
166192

167193
/**
168194
* Set the PathMatcher to use for String pattern matching.
169-
* <p>By default this is {@link AntPathMatcher}.
170-
* <p><strong>Note:</strong> This property is mutually exclusive with and
171-
* ignored when {@link #setPatternParser(PathPatternParser)} is set.
195+
* <p><strong>Note:</strong> This property is mutually exclusive with
196+
* {@link #setPatternParser(PathPatternParser)}. If set, it enables use of
197+
* String path matching, unless a {@code PathPatternParser} is also
198+
* explicitly set in which case this property is ignored.
199+
* <p>By default this is an instance of {@link AntPathMatcher} with default
200+
* settings.
172201
*/
173202
public PathMatchConfigurer setPathMatcher(PathMatcher pathMatcher) {
174203
this.pathMatcher = pathMatcher;
204+
this.preferPathMatcher = true;
175205
return this;
176206
}
177207

178208

209+
/**
210+
* Whether to prefer {@link PathMatcher}. This is the case when either is true:
211+
* <ul>
212+
* <li>{@link PathPatternParser} is explicitly set to {@code null}.
213+
* <li>{@link PathPatternParser} is not explicitly set, and a
214+
* {@link PathMatcher} related option is explicitly set.
215+
* </ul>
216+
* @since 6.0
217+
*/
218+
protected boolean preferPathMatcher() {
219+
return (this.patternParser == null && this.preferPathMatcher);
220+
}
221+
179222
/**
180223
* Return the {@link PathPatternParser} to use, if configured.
181224
* @since 5.3

0 commit comments

Comments
 (0)