17
17
package org .springframework .web .reactive .handler ;
18
18
19
19
import java .util .Collections ;
20
+ import java .util .Comparator ;
21
+ import java .util .LinkedHashMap ;
20
22
import java .util .Map ;
21
23
22
24
import reactor .core .publisher .Mono ;
25
27
import org .springframework .http .server .reactive .PathContainer ;
26
28
import org .springframework .lang .Nullable ;
27
29
import org .springframework .util .Assert ;
30
+ import org .springframework .util .StringUtils ;
28
31
import org .springframework .web .server .ServerWebExchange ;
29
32
import org .springframework .web .util .pattern .PathPattern ;
30
33
@@ -51,8 +54,7 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping {
51
54
52
55
private boolean lazyInitHandlers = false ;
53
56
54
- @ Nullable
55
- private PathPatternRegistry <Object > patternRegistry ;
57
+ private final Map <PathPattern , Object > handlerMap = new LinkedHashMap <>();
56
58
57
59
58
60
/**
@@ -70,12 +72,12 @@ public void setLazyInitHandlers(boolean lazyInitHandlers) {
70
72
}
71
73
72
74
/**
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 .
76
78
*/
77
79
public final Map <PathPattern , Object > getHandlerMap () {
78
- return ( this . patternRegistry != null ? this . patternRegistry . getPatternsMap () : Collections .emptyMap () );
80
+ return Collections .unmodifiableMap ( this . handlerMap );
79
81
}
80
82
81
83
@@ -111,25 +113,26 @@ else if (handler == null && logger.isTraceEnabled()) {
111
113
* @see org.springframework.web.util.pattern.PathPattern
112
114
*/
113
115
@ 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 );
129
132
}
130
133
131
134
private Object handleMatch (Object handler , PathPattern bestMatch , PathContainer pathWithinMapping ,
132
- ServerWebExchange exchange ) throws Exception {
135
+ ServerWebExchange exchange ) {
133
136
134
137
// Bean name or resolved handler?
135
138
if (handler instanceof String ) {
@@ -152,10 +155,9 @@ private Object handleMatch(Object handler, PathPattern bestMatch, PathContainer
152
155
* for example to enforce specific preconditions expressed in URL mappings.
153
156
* @param handler the handler object to validate
154
157
* @param exchange current exchange
155
- * @throws Exception if validation failed
156
158
*/
157
159
@ SuppressWarnings ("UnusedParameters" )
158
- protected void validateHandler (Object handler , ServerWebExchange exchange ) throws Exception {
160
+ protected void validateHandler (Object handler , ServerWebExchange exchange ) {
159
161
}
160
162
161
163
/**
@@ -165,7 +167,9 @@ protected void validateHandler(Object handler, ServerWebExchange exchange) throw
165
167
* @throws BeansException if the handler couldn't be registered
166
168
* @throws IllegalStateException if there is a conflicting handler registered
167
169
*/
168
- protected void registerHandler (String [] urlPaths , String beanName ) throws BeansException , IllegalStateException {
170
+ protected void registerHandler (String [] urlPaths , String beanName )
171
+ throws BeansException , IllegalStateException {
172
+
169
173
Assert .notNull (urlPaths , "URL path array must not be null" );
170
174
for (String urlPath : urlPaths ) {
171
175
registerHandler (urlPath , beanName );
@@ -180,11 +184,26 @@ protected void registerHandler(String[] urlPaths, String beanName) throws BeansE
180
184
* @throws BeansException if the handler couldn't be registered
181
185
* @throws IllegalStateException if there is a conflicting handler registered
182
186
*/
183
- protected void registerHandler (String urlPath , Object handler ) throws BeansException , IllegalStateException {
187
+ protected void registerHandler (String urlPath , Object handler )
188
+ throws BeansException , IllegalStateException {
189
+
184
190
Assert .notNull (urlPath , "URL path must not be null" );
185
191
Assert .notNull (handler , "Handler object must not be null" );
186
192
Object resolvedHandler = handler ;
187
193
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
+ }
188
207
189
208
// Eagerly resolve handler if referencing singleton via name.
190
209
if (!this .lazyInitHandlers && handler instanceof String ) {
@@ -193,32 +212,26 @@ protected void registerHandler(String urlPath, Object handler) throws BeansExcep
193
212
resolvedHandler = obtainApplicationContext ().getBean (handlerName );
194
213
}
195
214
}
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 ));
198
220
}
221
+ }
199
222
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 ;
210
226
}
211
227
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 ;
217
229
}
218
230
}
219
231
220
232
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 () + "]" );
222
235
}
223
236
224
237
}
0 commit comments