11/*
2- * Copyright 2002-2021 the original author or authors.
2+ * Copyright 2002-2023 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.
@@ -82,7 +82,7 @@ public class HandlerMappingIntrospector
8282 @ Nullable
8383 private List <HandlerMapping > handlerMappings ;
8484
85- private Map <HandlerMapping , PathPatternMatchableHandlerMapping > pathPatternHandlerMappings = Collections .emptyMap ();
85+ private Map <HandlerMapping , PathPatternMatchableHandlerMapping > pathPatternMappings = Collections .emptyMap ();
8686
8787
8888 /**
@@ -113,10 +113,55 @@ public void afterPropertiesSet() {
113113 if (this .handlerMappings == null ) {
114114 Assert .notNull (this .applicationContext , "No ApplicationContext" );
115115 this .handlerMappings = initHandlerMappings (this .applicationContext );
116- this .pathPatternHandlerMappings = initPathPatternMatchableHandlerMappings (this .handlerMappings );
116+
117+ this .pathPatternMappings = this .handlerMappings .stream ()
118+ .filter (m -> m instanceof MatchableHandlerMapping && ((MatchableHandlerMapping ) m ).getPatternParser () != null )
119+ .map (mapping -> (MatchableHandlerMapping ) mapping )
120+ .collect (Collectors .toMap (mapping -> mapping , PathPatternMatchableHandlerMapping ::new ));
117121 }
118122 }
119123
124+ private static List <HandlerMapping > initHandlerMappings (ApplicationContext context ) {
125+
126+ Map <String , HandlerMapping > beans =
127+ BeanFactoryUtils .beansOfTypeIncludingAncestors (context , HandlerMapping .class , true , false );
128+
129+ if (!beans .isEmpty ()) {
130+ List <HandlerMapping > mappings = new ArrayList <>(beans .values ());
131+ AnnotationAwareOrderComparator .sort (mappings );
132+ return Collections .unmodifiableList (mappings );
133+ }
134+
135+ return Collections .unmodifiableList (initFallback (context ));
136+ }
137+
138+ private static List <HandlerMapping > initFallback (ApplicationContext applicationContext ) {
139+ Properties properties ;
140+ try {
141+ Resource resource = new ClassPathResource ("DispatcherServlet.properties" , DispatcherServlet .class );
142+ properties = PropertiesLoaderUtils .loadProperties (resource );
143+ }
144+ catch (IOException ex ) {
145+ throw new IllegalStateException ("Could not load DispatcherServlet.properties: " + ex .getMessage ());
146+ }
147+
148+ String value = properties .getProperty (HandlerMapping .class .getName ());
149+ String [] names = StringUtils .commaDelimitedListToStringArray (value );
150+ List <HandlerMapping > result = new ArrayList <>(names .length );
151+ for (String name : names ) {
152+ try {
153+ Class <?> clazz = ClassUtils .forName (name , DispatcherServlet .class .getClassLoader ());
154+ Object mapping = applicationContext .getAutowireCapableBeanFactory ().createBean (clazz );
155+ result .add ((HandlerMapping ) mapping );
156+ }
157+ catch (ClassNotFoundException ex ) {
158+ throw new IllegalStateException ("Could not find default HandlerMapping [" + name + "]" );
159+ }
160+ }
161+ return result ;
162+ }
163+
164+
120165 /**
121166 * Return the configured or detected {@code HandlerMapping}s.
122167 */
@@ -127,27 +172,27 @@ public List<HandlerMapping> getHandlerMappings() {
127172
128173 /**
129174 * Find the {@link HandlerMapping} that would handle the given request and
130- * return it as a {@link MatchableHandlerMapping} that can be used to test
131- * request-matching criteria.
132- * <p>If the matching HandlerMapping is not an instance of
133- * {@link MatchableHandlerMapping}, an IllegalStateException is raised.
175+ * return a {@link MatchableHandlerMapping} to use for path matching.
134176 * @param request the current request
135- * @return the resolved matcher, or {@code null}
177+ * @return the resolved {@code MatchableHandlerMapping}, or {@code null}
178+ * @throws IllegalStateException if the matching HandlerMapping is not an
179+ * instance of {@link MatchableHandlerMapping}
136180 * @throws Exception if any of the HandlerMapping's raise an exception
137181 */
138182 @ Nullable
139183 public MatchableHandlerMapping getMatchableHandlerMapping (HttpServletRequest request ) throws Exception {
140184 HttpServletRequest wrappedRequest = new AttributesPreservingRequest (request );
141- return doWithMatchingMapping (wrappedRequest , false , (matchedMapping , executionChain ) -> {
142- if (matchedMapping instanceof MatchableHandlerMapping ) {
143- PathPatternMatchableHandlerMapping mapping = this .pathPatternHandlerMappings .get (matchedMapping );
144- if (mapping != null ) {
185+
186+ return doWithHandlerMapping (wrappedRequest , false , (mapping , executionChain ) -> {
187+ if (mapping instanceof MatchableHandlerMapping ) {
188+ PathPatternMatchableHandlerMapping pathPatternMapping = this .pathPatternMappings .get (mapping );
189+ if (pathPatternMapping != null ) {
145190 RequestPath requestPath = ServletRequestPathUtils .getParsedRequestPath (wrappedRequest );
146- return new PathSettingHandlerMapping ( mapping , requestPath );
191+ return new LookupPathMatchableHandlerMapping ( pathPatternMapping , requestPath );
147192 }
148193 else {
149194 String lookupPath = (String ) wrappedRequest .getAttribute (UrlPathHelper .PATH_ATTRIBUTE );
150- return new PathSettingHandlerMapping ((MatchableHandlerMapping ) matchedMapping , lookupPath );
195+ return new LookupPathMatchableHandlerMapping ((MatchableHandlerMapping ) mapping , lookupPath );
151196 }
152197 }
153198 throw new IllegalStateException ("HandlerMapping is not a MatchableHandlerMapping" );
@@ -158,7 +203,7 @@ public MatchableHandlerMapping getMatchableHandlerMapping(HttpServletRequest req
158203 @ Nullable
159204 public CorsConfiguration getCorsConfiguration (HttpServletRequest request ) {
160205 AttributesPreservingRequest wrappedRequest = new AttributesPreservingRequest (request );
161- return doWithMatchingMappingIgnoringException (wrappedRequest , (handlerMapping , executionChain ) -> {
206+ return doWithHandlerMappingIgnoringException (wrappedRequest , (handlerMapping , executionChain ) -> {
162207 for (HandlerInterceptor interceptor : executionChain .getInterceptorList ()) {
163208 if (interceptor instanceof CorsConfigurationSource ) {
164209 return ((CorsConfigurationSource ) interceptor ).getCorsConfiguration (wrappedRequest );
@@ -172,15 +217,15 @@ public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
172217 }
173218
174219 @ Nullable
175- private <T > T doWithMatchingMapping (
220+ private <T > T doWithHandlerMapping (
176221 HttpServletRequest request , boolean ignoreException ,
177- BiFunction <HandlerMapping , HandlerExecutionChain , T > matchHandler ) throws Exception {
222+ BiFunction <HandlerMapping , HandlerExecutionChain , T > extractor ) throws Exception {
178223
179- Assert .notNull (this .handlerMappings , "Handler mappings not initialized" );
224+ Assert .state (this .handlerMappings != null , "HandlerMapping's not initialized" );
180225
181- boolean parseRequestPath = !this .pathPatternHandlerMappings .isEmpty ();
226+ boolean parsePath = !this .pathPatternMappings .isEmpty ();
182227 RequestPath previousPath = null ;
183- if (parseRequestPath ) {
228+ if (parsePath ) {
184229 previousPath = (RequestPath ) request .getAttribute (ServletRequestPathUtils .PATH_ATTRIBUTE );
185230 ServletRequestPathUtils .parseAndCache (request );
186231 }
@@ -198,79 +243,30 @@ private <T> T doWithMatchingMapping(
198243 if (chain == null ) {
199244 continue ;
200245 }
201- return matchHandler .apply (handlerMapping , chain );
246+ return extractor .apply (handlerMapping , chain );
202247 }
203248 }
204249 finally {
205- if (parseRequestPath ) {
250+ if (parsePath ) {
206251 ServletRequestPathUtils .setParsedRequestPath (previousPath , request );
207252 }
208253 }
209254 return null ;
210255 }
211256
212257 @ Nullable
213- private <T > T doWithMatchingMappingIgnoringException (
258+ private <T > T doWithHandlerMappingIgnoringException (
214259 HttpServletRequest request , BiFunction <HandlerMapping , HandlerExecutionChain , T > matchHandler ) {
215260
216261 try {
217- return doWithMatchingMapping (request , true , matchHandler );
262+ return doWithHandlerMapping (request , true , matchHandler );
218263 }
219264 catch (Exception ex ) {
220265 throw new IllegalStateException ("HandlerMapping exception not suppressed" , ex );
221266 }
222267 }
223268
224269
225- private static List <HandlerMapping > initHandlerMappings (ApplicationContext applicationContext ) {
226- Map <String , HandlerMapping > beans = BeanFactoryUtils .beansOfTypeIncludingAncestors (
227- applicationContext , HandlerMapping .class , true , false );
228- if (!beans .isEmpty ()) {
229- List <HandlerMapping > mappings = new ArrayList <>(beans .values ());
230- AnnotationAwareOrderComparator .sort (mappings );
231- return Collections .unmodifiableList (mappings );
232- }
233- return Collections .unmodifiableList (initFallback (applicationContext ));
234- }
235-
236- private static List <HandlerMapping > initFallback (ApplicationContext applicationContext ) {
237- Properties props ;
238- String path = "DispatcherServlet.properties" ;
239- try {
240- Resource resource = new ClassPathResource (path , DispatcherServlet .class );
241- props = PropertiesLoaderUtils .loadProperties (resource );
242- }
243- catch (IOException ex ) {
244- throw new IllegalStateException ("Could not load '" + path + "': " + ex .getMessage ());
245- }
246-
247- String value = props .getProperty (HandlerMapping .class .getName ());
248- String [] names = StringUtils .commaDelimitedListToStringArray (value );
249- List <HandlerMapping > result = new ArrayList <>(names .length );
250- for (String name : names ) {
251- try {
252- Class <?> clazz = ClassUtils .forName (name , DispatcherServlet .class .getClassLoader ());
253- Object mapping = applicationContext .getAutowireCapableBeanFactory ().createBean (clazz );
254- result .add ((HandlerMapping ) mapping );
255- }
256- catch (ClassNotFoundException ex ) {
257- throw new IllegalStateException ("Could not find default HandlerMapping [" + name + "]" );
258- }
259- }
260- return result ;
261- }
262-
263- private static Map <HandlerMapping , PathPatternMatchableHandlerMapping > initPathPatternMatchableHandlerMappings (
264- List <HandlerMapping > mappings ) {
265-
266- return mappings .stream ()
267- .filter (mapping -> mapping instanceof MatchableHandlerMapping )
268- .map (mapping -> (MatchableHandlerMapping ) mapping )
269- .filter (mapping -> mapping .getPatternParser () != null )
270- .collect (Collectors .toMap (mapping -> mapping , PathPatternMatchableHandlerMapping ::new ));
271- }
272-
273-
274270 /**
275271 * Request wrapper that buffers request attributes in order protect the
276272 * underlying request from attribute changes.
@@ -316,26 +312,27 @@ public void removeAttribute(String name) {
316312 }
317313
318314
319- private static class PathSettingHandlerMapping implements MatchableHandlerMapping {
315+ private static class LookupPathMatchableHandlerMapping implements MatchableHandlerMapping {
320316
321317 private final MatchableHandlerMapping delegate ;
322318
323- private final Object path ;
319+ private final Object lookupPath ;
324320
325321 private final String pathAttributeName ;
326322
327- PathSettingHandlerMapping (MatchableHandlerMapping delegate , Object path ) {
323+ LookupPathMatchableHandlerMapping (MatchableHandlerMapping delegate , Object lookupPath ) {
328324 this .delegate = delegate ;
329- this .path = path ;
330- this .pathAttributeName = (path instanceof RequestPath ?
325+ this .lookupPath = lookupPath ;
326+ this .pathAttributeName = (lookupPath instanceof RequestPath ?
331327 ServletRequestPathUtils .PATH_ATTRIBUTE : UrlPathHelper .PATH_ATTRIBUTE );
332328 }
333329
334330 @ Nullable
335331 @ Override
336332 public RequestMatchResult match (HttpServletRequest request , String pattern ) {
333+ pattern = (StringUtils .hasLength (pattern ) && !pattern .startsWith ("/" ) ? "/" + pattern : pattern );
337334 Object previousPath = request .getAttribute (this .pathAttributeName );
338- request .setAttribute (this .pathAttributeName , this .path );
335+ request .setAttribute (this .pathAttributeName , this .lookupPath );
339336 try {
340337 return this .delegate .match (request , pattern );
341338 }
0 commit comments