@@ -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 @ Override
@@ -95,10 +95,55 @@ public void afterPropertiesSet() {
9595 if (this .handlerMappings == null ) {
9696 Assert .notNull (this .applicationContext , "No ApplicationContext" );
9797 this .handlerMappings = initHandlerMappings (this .applicationContext );
98- this .pathPatternHandlerMappings = initPathPatternMatchableHandlerMappings (this .handlerMappings );
98+
99+ this .pathPatternMappings = this .handlerMappings .stream ()
100+ .filter (m -> m instanceof MatchableHandlerMapping hm && hm .getPatternParser () != null )
101+ .map (mapping -> (MatchableHandlerMapping ) mapping )
102+ .collect (Collectors .toMap (mapping -> mapping , PathPatternMatchableHandlerMapping ::new ));
99103 }
100104 }
101105
106+ private static List <HandlerMapping > initHandlerMappings (ApplicationContext context ) {
107+
108+ Map <String , HandlerMapping > beans =
109+ BeanFactoryUtils .beansOfTypeIncludingAncestors (context , HandlerMapping .class , true , false );
110+
111+ if (!beans .isEmpty ()) {
112+ List <HandlerMapping > mappings = new ArrayList <>(beans .values ());
113+ AnnotationAwareOrderComparator .sort (mappings );
114+ return Collections .unmodifiableList (mappings );
115+ }
116+
117+ return Collections .unmodifiableList (initFallback (context ));
118+ }
119+
120+ private static List <HandlerMapping > initFallback (ApplicationContext applicationContext ) {
121+ Properties properties ;
122+ try {
123+ Resource resource = new ClassPathResource ("DispatcherServlet.properties" , DispatcherServlet .class );
124+ properties = PropertiesLoaderUtils .loadProperties (resource );
125+ }
126+ catch (IOException ex ) {
127+ throw new IllegalStateException ("Could not load DispatcherServlet.properties: " + ex .getMessage ());
128+ }
129+
130+ String value = properties .getProperty (HandlerMapping .class .getName ());
131+ String [] names = StringUtils .commaDelimitedListToStringArray (value );
132+ List <HandlerMapping > result = new ArrayList <>(names .length );
133+ for (String name : names ) {
134+ try {
135+ Class <?> clazz = ClassUtils .forName (name , DispatcherServlet .class .getClassLoader ());
136+ Object mapping = applicationContext .getAutowireCapableBeanFactory ().createBean (clazz );
137+ result .add ((HandlerMapping ) mapping );
138+ }
139+ catch (ClassNotFoundException ex ) {
140+ throw new IllegalStateException ("Could not find default HandlerMapping [" + name + "]" );
141+ }
142+ }
143+ return result ;
144+ }
145+
146+
102147 /**
103148 * Return the configured or detected {@code HandlerMapping}s.
104149 */
@@ -109,27 +154,27 @@ public List<HandlerMapping> getHandlerMappings() {
109154
110155 /**
111156 * Find the {@link HandlerMapping} that would handle the given request and
112- * return it as a {@link MatchableHandlerMapping} that can be used to test
113- * request-matching criteria.
114- * <p>If the matching HandlerMapping is not an instance of
115- * {@link MatchableHandlerMapping}, an IllegalStateException is raised.
157+ * return a {@link MatchableHandlerMapping} to use for path matching.
116158 * @param request the current request
117- * @return the resolved matcher, or {@code null}
159+ * @return the resolved {@code MatchableHandlerMapping}, or {@code null}
160+ * @throws IllegalStateException if the matching HandlerMapping is not an
161+ * instance of {@link MatchableHandlerMapping}
118162 * @throws Exception if any of the HandlerMapping's raise an exception
119163 */
120164 @ Nullable
121165 public MatchableHandlerMapping getMatchableHandlerMapping (HttpServletRequest request ) throws Exception {
122166 HttpServletRequest wrappedRequest = new AttributesPreservingRequest (request );
123- return doWithMatchingMapping (wrappedRequest , false , (matchedMapping , executionChain ) -> {
124- if (matchedMapping instanceof MatchableHandlerMapping matchableHandlerMapping ) {
125- PathPatternMatchableHandlerMapping mapping = this .pathPatternHandlerMappings .get (matchedMapping );
126- if (mapping != null ) {
167+
168+ return doWithHandlerMapping (wrappedRequest , false , (mapping , executionChain ) -> {
169+ if (mapping instanceof MatchableHandlerMapping ) {
170+ PathPatternMatchableHandlerMapping pathPatternMapping = this .pathPatternMappings .get (mapping );
171+ if (pathPatternMapping != null ) {
127172 RequestPath requestPath = ServletRequestPathUtils .getParsedRequestPath (wrappedRequest );
128- return new PathSettingHandlerMapping ( mapping , requestPath );
173+ return new LookupPathMatchableHandlerMapping ( pathPatternMapping , requestPath );
129174 }
130175 else {
131176 String lookupPath = (String ) wrappedRequest .getAttribute (UrlPathHelper .PATH_ATTRIBUTE );
132- return new PathSettingHandlerMapping ( matchableHandlerMapping , lookupPath );
177+ return new LookupPathMatchableHandlerMapping (( MatchableHandlerMapping ) mapping , lookupPath );
133178 }
134179 }
135180 throw new IllegalStateException ("HandlerMapping is not a MatchableHandlerMapping" );
@@ -140,7 +185,7 @@ public MatchableHandlerMapping getMatchableHandlerMapping(HttpServletRequest req
140185 @ Nullable
141186 public CorsConfiguration getCorsConfiguration (HttpServletRequest request ) {
142187 AttributesPreservingRequest wrappedRequest = new AttributesPreservingRequest (request );
143- return doWithMatchingMappingIgnoringException (wrappedRequest , (handlerMapping , executionChain ) -> {
188+ return doWithHandlerMappingIgnoringException (wrappedRequest , (handlerMapping , executionChain ) -> {
144189 for (HandlerInterceptor interceptor : executionChain .getInterceptorList ()) {
145190 if (interceptor instanceof CorsConfigurationSource ccs ) {
146191 return ccs .getCorsConfiguration (wrappedRequest );
@@ -154,15 +199,15 @@ public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
154199 }
155200
156201 @ Nullable
157- private <T > T doWithMatchingMapping (
202+ private <T > T doWithHandlerMapping (
158203 HttpServletRequest request , boolean ignoreException ,
159- BiFunction <HandlerMapping , HandlerExecutionChain , T > matchHandler ) throws Exception {
204+ BiFunction <HandlerMapping , HandlerExecutionChain , T > extractor ) throws Exception {
160205
161- Assert .state (this .handlerMappings != null , "Handler mappings not initialized" );
206+ Assert .state (this .handlerMappings != null , "HandlerMapping's not initialized" );
162207
163- boolean parseRequestPath = !this .pathPatternHandlerMappings .isEmpty ();
208+ boolean parsePath = !this .pathPatternMappings .isEmpty ();
164209 RequestPath previousPath = null ;
165- if (parseRequestPath ) {
210+ if (parsePath ) {
166211 previousPath = (RequestPath ) request .getAttribute (ServletRequestPathUtils .PATH_ATTRIBUTE );
167212 ServletRequestPathUtils .parseAndCache (request );
168213 }
@@ -180,79 +225,30 @@ private <T> T doWithMatchingMapping(
180225 if (chain == null ) {
181226 continue ;
182227 }
183- return matchHandler .apply (handlerMapping , chain );
228+ return extractor .apply (handlerMapping , chain );
184229 }
185230 }
186231 finally {
187- if (parseRequestPath ) {
232+ if (parsePath ) {
188233 ServletRequestPathUtils .setParsedRequestPath (previousPath , request );
189234 }
190235 }
191236 return null ;
192237 }
193238
194239 @ Nullable
195- private <T > T doWithMatchingMappingIgnoringException (
240+ private <T > T doWithHandlerMappingIgnoringException (
196241 HttpServletRequest request , BiFunction <HandlerMapping , HandlerExecutionChain , T > matchHandler ) {
197242
198243 try {
199- return doWithMatchingMapping (request , true , matchHandler );
244+ return doWithHandlerMapping (request , true , matchHandler );
200245 }
201246 catch (Exception ex ) {
202247 throw new IllegalStateException ("HandlerMapping exception not suppressed" , ex );
203248 }
204249 }
205250
206251
207- private static List <HandlerMapping > initHandlerMappings (ApplicationContext applicationContext ) {
208- Map <String , HandlerMapping > beans = BeanFactoryUtils .beansOfTypeIncludingAncestors (
209- applicationContext , HandlerMapping .class , true , false );
210- if (!beans .isEmpty ()) {
211- List <HandlerMapping > mappings = new ArrayList <>(beans .values ());
212- AnnotationAwareOrderComparator .sort (mappings );
213- return Collections .unmodifiableList (mappings );
214- }
215- return Collections .unmodifiableList (initFallback (applicationContext ));
216- }
217-
218- private static List <HandlerMapping > initFallback (ApplicationContext applicationContext ) {
219- Properties props ;
220- String path = "DispatcherServlet.properties" ;
221- try {
222- Resource resource = new ClassPathResource (path , DispatcherServlet .class );
223- props = PropertiesLoaderUtils .loadProperties (resource );
224- }
225- catch (IOException ex ) {
226- throw new IllegalStateException ("Could not load '" + path + "': " + ex .getMessage ());
227- }
228-
229- String value = props .getProperty (HandlerMapping .class .getName ());
230- String [] names = StringUtils .commaDelimitedListToStringArray (value );
231- List <HandlerMapping > result = new ArrayList <>(names .length );
232- for (String name : names ) {
233- try {
234- Class <?> clazz = ClassUtils .forName (name , DispatcherServlet .class .getClassLoader ());
235- Object mapping = applicationContext .getAutowireCapableBeanFactory ().createBean (clazz );
236- result .add ((HandlerMapping ) mapping );
237- }
238- catch (ClassNotFoundException ex ) {
239- throw new IllegalStateException ("Could not find default HandlerMapping [" + name + "]" );
240- }
241- }
242- return result ;
243- }
244-
245- private static Map <HandlerMapping , PathPatternMatchableHandlerMapping > initPathPatternMatchableHandlerMappings (
246- List <HandlerMapping > mappings ) {
247-
248- return mappings .stream ()
249- .filter (MatchableHandlerMapping .class ::isInstance )
250- .map (MatchableHandlerMapping .class ::cast )
251- .filter (mapping -> mapping .getPatternParser () != null )
252- .collect (Collectors .toMap (mapping -> mapping , PathPatternMatchableHandlerMapping ::new ));
253- }
254-
255-
256252 /**
257253 * Request wrapper that buffers request attributes in order protect the
258254 * underlying request from attribute changes.
@@ -298,26 +294,27 @@ public void removeAttribute(String name) {
298294 }
299295
300296
301- private static class PathSettingHandlerMapping implements MatchableHandlerMapping {
297+ private static class LookupPathMatchableHandlerMapping implements MatchableHandlerMapping {
302298
303299 private final MatchableHandlerMapping delegate ;
304300
305- private final Object path ;
301+ private final Object lookupPath ;
306302
307303 private final String pathAttributeName ;
308304
309- PathSettingHandlerMapping (MatchableHandlerMapping delegate , Object path ) {
305+ LookupPathMatchableHandlerMapping (MatchableHandlerMapping delegate , Object lookupPath ) {
310306 this .delegate = delegate ;
311- this .path = path ;
312- this .pathAttributeName = (path instanceof RequestPath ?
307+ this .lookupPath = lookupPath ;
308+ this .pathAttributeName = (lookupPath instanceof RequestPath ?
313309 ServletRequestPathUtils .PATH_ATTRIBUTE : UrlPathHelper .PATH_ATTRIBUTE );
314310 }
315311
316312 @ Nullable
317313 @ Override
318314 public RequestMatchResult match (HttpServletRequest request , String pattern ) {
315+ pattern = (StringUtils .hasLength (pattern ) && !pattern .startsWith ("/" ) ? "/" + pattern : pattern );
319316 Object previousPath = request .getAttribute (this .pathAttributeName );
320- request .setAttribute (this .pathAttributeName , this .path );
317+ request .setAttribute (this .pathAttributeName , this .lookupPath );
321318 try {
322319 return this .delegate .match (request , pattern );
323320 }
0 commit comments