1717package org .springframework .web .reactive .handler ;
1818
1919import java .util .Collections ;
20+ import java .util .Comparator ;
21+ import java .util .LinkedHashMap ;
2022import java .util .Map ;
2123
2224import reactor .core .publisher .Mono ;
2527import org .springframework .http .server .reactive .PathContainer ;
2628import org .springframework .lang .Nullable ;
2729import org .springframework .util .Assert ;
30+ import org .springframework .util .StringUtils ;
2831import org .springframework .web .server .ServerWebExchange ;
2932import org .springframework .web .util .pattern .PathPattern ;
3033
@@ -51,8 +54,7 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping {
5154
5255 private boolean lazyInitHandlers = false ;
5356
54- @ Nullable
55- private PathPatternRegistry <Object > patternRegistry ;
57+ private final Map <PathPattern , Object > handlerMap = new LinkedHashMap <>();
5658
5759
5860 /**
@@ -70,12 +72,12 @@ public void setLazyInitHandlers(boolean lazyInitHandlers) {
7072 }
7173
7274 /**
73- * Return the registered handlers as an unmodifiable Map, with the registered path
74- * pattern as key and the handler object ( or handler bean name in case of a lazy-init handler)
75- * as value .
75+ * Return a read-only view of registered path patterns and handlers which may
76+ * may be an actual handler instance or the bean name of lazily initialized
77+ * handler .
7678 */
7779 public final Map <PathPattern , Object > getHandlerMap () {
78- return ( this . patternRegistry != null ? this . patternRegistry . getPatternsMap () : Collections .emptyMap () );
80+ return Collections .unmodifiableMap ( this . handlerMap );
7981 }
8082
8183
@@ -111,25 +113,26 @@ else if (handler == null && logger.isTraceEnabled()) {
111113 * @see org.springframework.web.util.pattern.PathPattern
112114 */
113115 @ Nullable
114- protected Object lookupHandler (PathContainer lookupPath , ServerWebExchange exchange ) throws Exception {
115- if (this .patternRegistry != null ) {
116- PathMatchResult <Object > bestMatch = this .patternRegistry .findFirstMatch (lookupPath );
117- if (bestMatch != null ) {
118- if (logger .isDebugEnabled ()) {
119- logger .debug ("Matching patterns for request [" + lookupPath + "] are " + bestMatch );
120- }
121- PathContainer pathWithinMapping = bestMatch .getPattern ().extractPathWithinPattern (lookupPath );
122- Object handler = bestMatch .getHandler ();
123- return handleMatch (handler , bestMatch .getPattern (), pathWithinMapping , exchange );
124- }
125- }
126-
127- // No handler found...
128- return null ;
116+ protected Object lookupHandler (PathContainer lookupPath , ServerWebExchange exchange )
117+ throws Exception {
118+
119+ return this .handlerMap .entrySet ().stream ()
120+ .filter (entry -> entry .getKey ().matches (lookupPath ))
121+ .sorted (Comparator .comparing (Map .Entry ::getKey ))
122+ .findFirst ()
123+ .map (entry -> {
124+ PathPattern pattern = entry .getKey ();
125+ if (logger .isDebugEnabled ()) {
126+ logger .debug ("Matching pattern for request [" + lookupPath + "] is " + pattern );
127+ }
128+ PathContainer pathWithinMapping = pattern .extractPathWithinPattern (lookupPath );
129+ return handleMatch (entry .getValue (), pattern , pathWithinMapping , exchange );
130+ })
131+ .orElse (null );
129132 }
130133
131134 private Object handleMatch (Object handler , PathPattern bestMatch , PathContainer pathWithinMapping ,
132- ServerWebExchange exchange ) throws Exception {
135+ ServerWebExchange exchange ) {
133136
134137 // Bean name or resolved handler?
135138 if (handler instanceof String ) {
@@ -152,10 +155,9 @@ private Object handleMatch(Object handler, PathPattern bestMatch, PathContainer
152155 * for example to enforce specific preconditions expressed in URL mappings.
153156 * @param handler the handler object to validate
154157 * @param exchange current exchange
155- * @throws Exception if validation failed
156158 */
157159 @ SuppressWarnings ("UnusedParameters" )
158- protected void validateHandler (Object handler , ServerWebExchange exchange ) throws Exception {
160+ protected void validateHandler (Object handler , ServerWebExchange exchange ) {
159161 }
160162
161163 /**
@@ -165,7 +167,9 @@ protected void validateHandler(Object handler, ServerWebExchange exchange) throw
165167 * @throws BeansException if the handler couldn't be registered
166168 * @throws IllegalStateException if there is a conflicting handler registered
167169 */
168- protected void registerHandler (String [] urlPaths , String beanName ) throws BeansException , IllegalStateException {
170+ protected void registerHandler (String [] urlPaths , String beanName )
171+ throws BeansException , IllegalStateException {
172+
169173 Assert .notNull (urlPaths , "URL path array must not be null" );
170174 for (String urlPath : urlPaths ) {
171175 registerHandler (urlPath , beanName );
@@ -180,11 +184,26 @@ protected void registerHandler(String[] urlPaths, String beanName) throws BeansE
180184 * @throws BeansException if the handler couldn't be registered
181185 * @throws IllegalStateException if there is a conflicting handler registered
182186 */
183- protected void registerHandler (String urlPath , Object handler ) throws BeansException , IllegalStateException {
187+ protected void registerHandler (String urlPath , Object handler )
188+ throws BeansException , IllegalStateException {
189+
184190 Assert .notNull (urlPath , "URL path must not be null" );
185191 Assert .notNull (handler , "Handler object must not be null" );
186192 Object resolvedHandler = handler ;
187193
194+ // Parse path pattern
195+ urlPath = prependLeadingSlash (urlPath );
196+ PathPattern pattern = getPathPatternParser ().parse (urlPath );
197+ if (this .handlerMap .containsKey (pattern )) {
198+ Object existingHandler = this .handlerMap .get (pattern );
199+ if (existingHandler != null ) {
200+ if (existingHandler != resolvedHandler ) {
201+ throw new IllegalStateException (
202+ "Cannot map " + getHandlerDescription (handler ) + " to [" + urlPath + "]: " +
203+ "there is already " + getHandlerDescription (existingHandler ) + " mapped." );
204+ }
205+ }
206+ }
188207
189208 // Eagerly resolve handler if referencing singleton via name.
190209 if (!this .lazyInitHandlers && handler instanceof String ) {
@@ -193,32 +212,26 @@ protected void registerHandler(String urlPath, Object handler) throws BeansExcep
193212 resolvedHandler = obtainApplicationContext ().getBean (handlerName );
194213 }
195214 }
196- if (this .patternRegistry == null ) {
197- this .patternRegistry = new PathPatternRegistry <>(getPathPatternParser ());
215+
216+ // Register resolved handler
217+ this .handlerMap .put (pattern , resolvedHandler );
218+ if (logger .isInfoEnabled ()) {
219+ logger .info ("Mapped URL path [" + urlPath + "] onto " + getHandlerDescription (handler ));
198220 }
221+ }
199222
200- Map <PathPattern , Object > patternsMap = this .patternRegistry .getPatternsMap ();
201- if (patternsMap .containsKey (urlPath )) {
202- Object mappedHandler = patternsMap .get (urlPath );
203- if (mappedHandler != null ) {
204- if (mappedHandler != resolvedHandler ) {
205- throw new IllegalStateException (
206- "Cannot map " + getHandlerDescription (handler ) + " to URL path [" + urlPath +
207- "]: There is already " + getHandlerDescription (mappedHandler ) + " mapped." );
208- }
209- }
223+ private static String prependLeadingSlash (String pattern ) {
224+ if (StringUtils .hasLength (pattern ) && !pattern .startsWith ("/" )) {
225+ return "/" + pattern ;
210226 }
211227 else {
212- this .patternRegistry .register (urlPath , resolvedHandler );
213- }
214-
215- if (logger .isInfoEnabled ()) {
216- logger .info ("Mapped URL path [" + urlPath + "] onto " + getHandlerDescription (handler ));
228+ return pattern ;
217229 }
218230 }
219231
220232 private String getHandlerDescription (Object handler ) {
221- return "handler " + (handler instanceof String ? "'" + handler + "'" : "of type [" + handler .getClass () + "]" );
233+ return "handler " + (handler instanceof String ?
234+ "'" + handler + "'" : "of type [" + handler .getClass () + "]" );
222235 }
223236
224237}
0 commit comments