Support for direct path lookups in WebFlux
Closes gh-22961
This commit is contained in:
@@ -381,15 +381,13 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
|
||||
@Nullable
|
||||
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
|
||||
List<Match> matches = new ArrayList<>();
|
||||
List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
|
||||
List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
|
||||
if (directPathMatches != null) {
|
||||
addMatchingMappings(directPathMatches, matches, request);
|
||||
}
|
||||
if (matches.isEmpty()) {
|
||||
// No choice but to go through all mappings...
|
||||
addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
|
||||
}
|
||||
|
||||
if (!matches.isEmpty()) {
|
||||
Match bestMatch = matches.get(0);
|
||||
if (matches.size() > 1) {
|
||||
@@ -503,7 +501,9 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
|
||||
* {@link #getDirectPaths(Object)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
protected abstract Set<String> getMappingPathPatterns(T mapping);
|
||||
protected Set<String> getMappingPathPatterns(T mapping) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the request mapping paths that are not patterns.
|
||||
@@ -550,7 +550,7 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
|
||||
|
||||
private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();
|
||||
|
||||
private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();
|
||||
private final MultiValueMap<String, T> pathLookup = new LinkedMultiValueMap<>();
|
||||
|
||||
private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();
|
||||
|
||||
@@ -571,8 +571,8 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
|
||||
* @see #acquireReadLock()
|
||||
*/
|
||||
@Nullable
|
||||
public List<T> getMappingsByUrl(String urlPath) {
|
||||
return this.urlLookup.get(urlPath);
|
||||
public List<T> getMappingsByDirectPath(String urlPath) {
|
||||
return this.pathLookup.get(urlPath);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -619,9 +619,9 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
|
||||
validateMethodMapping(handlerMethod, mapping);
|
||||
this.mappingLookup.put(mapping, handlerMethod);
|
||||
|
||||
Set<String> directUrls = AbstractHandlerMethodMapping.this.getDirectPaths(mapping);
|
||||
for (String url : directUrls) {
|
||||
this.urlLookup.add(url, mapping);
|
||||
Set<String> directPaths = AbstractHandlerMethodMapping.this.getDirectPaths(mapping);
|
||||
for (String path : directPaths) {
|
||||
this.pathLookup.add(path, mapping);
|
||||
}
|
||||
|
||||
String name = null;
|
||||
@@ -636,7 +636,7 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
|
||||
this.corsLookup.put(handlerMethod, config);
|
||||
}
|
||||
|
||||
this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
|
||||
this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directPaths, name));
|
||||
}
|
||||
finally {
|
||||
this.readWriteLock.writeLock().unlock();
|
||||
@@ -675,26 +675,26 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
|
||||
public void unregister(T mapping) {
|
||||
this.readWriteLock.writeLock().lock();
|
||||
try {
|
||||
MappingRegistration<T> definition = this.registry.remove(mapping);
|
||||
if (definition == null) {
|
||||
MappingRegistration<T> registration = this.registry.remove(mapping);
|
||||
if (registration == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.mappingLookup.remove(definition.getMapping());
|
||||
this.mappingLookup.remove(registration.getMapping());
|
||||
|
||||
for (String url : definition.getDirectUrls()) {
|
||||
List<T> list = this.urlLookup.get(url);
|
||||
if (list != null) {
|
||||
list.remove(definition.getMapping());
|
||||
if (list.isEmpty()) {
|
||||
this.urlLookup.remove(url);
|
||||
for (String path : registration.getDirectPaths()) {
|
||||
List<T> mappings = this.pathLookup.get(path);
|
||||
if (mappings != null) {
|
||||
mappings.remove(registration.getMapping());
|
||||
if (mappings.isEmpty()) {
|
||||
this.pathLookup.remove(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
removeMappingName(definition);
|
||||
removeMappingName(registration);
|
||||
|
||||
this.corsLookup.remove(definition.getHandlerMethod());
|
||||
this.corsLookup.remove(registration.getHandlerMethod());
|
||||
}
|
||||
finally {
|
||||
this.readWriteLock.writeLock().unlock();
|
||||
@@ -732,19 +732,19 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
|
||||
|
||||
private final HandlerMethod handlerMethod;
|
||||
|
||||
private final Set<String> directUrls;
|
||||
private final Set<String> directPaths;
|
||||
|
||||
@Nullable
|
||||
private final String mappingName;
|
||||
|
||||
public MappingRegistration(T mapping, HandlerMethod handlerMethod,
|
||||
@Nullable Set<String> directUrls, @Nullable String mappingName) {
|
||||
@Nullable Set<String> directPaths, @Nullable String mappingName) {
|
||||
|
||||
Assert.notNull(mapping, "Mapping must not be null");
|
||||
Assert.notNull(handlerMethod, "HandlerMethod must not be null");
|
||||
this.mapping = mapping;
|
||||
this.handlerMethod = handlerMethod;
|
||||
this.directUrls = (directUrls != null ? directUrls : Collections.emptySet());
|
||||
this.directPaths = (directPaths != null ? directPaths : Collections.emptySet());
|
||||
this.mappingName = mappingName;
|
||||
}
|
||||
|
||||
@@ -756,8 +756,8 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
|
||||
return this.handlerMethod;
|
||||
}
|
||||
|
||||
public Set<String> getDirectUrls() {
|
||||
return this.directUrls;
|
||||
public Set<String> getDirectPaths() {
|
||||
return this.directPaths;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.web.servlet.handler;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
@@ -51,7 +52,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
@SuppressWarnings("unused")
|
||||
public class HandlerMethodMappingTests {
|
||||
|
||||
private AbstractHandlerMethodMapping<String> mapping;
|
||||
private MyHandlerMethodMapping mapping;
|
||||
|
||||
private MyHandler handler;
|
||||
|
||||
@@ -78,13 +79,15 @@ public class HandlerMethodMappingTests {
|
||||
|
||||
@Test
|
||||
public void directMatch() throws Exception {
|
||||
String key = "foo";
|
||||
this.mapping.registerMapping(key, this.handler, this.method1);
|
||||
this.mapping.registerMapping("/foo", this.handler, this.method1);
|
||||
this.mapping.registerMapping("/fo*", this.handler, this.method2);
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", key);
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo");
|
||||
HandlerMethod result = this.mapping.getHandlerInternal(request);
|
||||
|
||||
assertThat(result.getMethod()).isEqualTo(method1);
|
||||
assertThat(request.getAttribute(HandlerMapping.BEST_MATCHING_HANDLER_ATTRIBUTE)).isEqualTo(result);
|
||||
assertThat(this.mapping.getMatches()).containsExactly("/foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -99,7 +102,7 @@ public class HandlerMethodMappingTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ambiguousMatch() throws Exception {
|
||||
public void ambiguousMatch() {
|
||||
this.mapping.registerMapping("/f?o", this.handler, this.method1);
|
||||
this.mapping.registerMapping("/fo?", this.handler, this.method2);
|
||||
|
||||
@@ -127,7 +130,7 @@ public class HandlerMethodMappingTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void registerMapping() throws Exception {
|
||||
public void registerMapping() {
|
||||
|
||||
String key1 = "/foo";
|
||||
String key2 = "/foo*";
|
||||
@@ -136,7 +139,7 @@ public class HandlerMethodMappingTests {
|
||||
|
||||
// Direct URL lookup
|
||||
|
||||
List<String> directUrlMatches = this.mapping.getMappingRegistry().getMappingsByUrl(key1);
|
||||
List<String> directUrlMatches = this.mapping.getMappingRegistry().getMappingsByDirectPath(key1);
|
||||
assertThat(directUrlMatches).isNotNull();
|
||||
assertThat(directUrlMatches.size()).isEqualTo(1);
|
||||
assertThat(directUrlMatches.get(0)).isEqualTo(key1);
|
||||
@@ -170,7 +173,7 @@ public class HandlerMethodMappingTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void registerMappingWithSameMethodAndTwoHandlerInstances() throws Exception {
|
||||
public void registerMappingWithSameMethodAndTwoHandlerInstances() {
|
||||
|
||||
String key1 = "foo";
|
||||
String key2 = "bar";
|
||||
@@ -186,7 +189,7 @@ public class HandlerMethodMappingTests {
|
||||
|
||||
// Direct URL lookup
|
||||
|
||||
List<String> directUrlMatches = this.mapping.getMappingRegistry().getMappingsByUrl(key1);
|
||||
List<String> directUrlMatches = this.mapping.getMappingRegistry().getMappingsByDirectPath(key1);
|
||||
assertThat(directUrlMatches).isNotNull();
|
||||
assertThat(directUrlMatches.size()).isEqualTo(1);
|
||||
assertThat(directUrlMatches.get(0)).isEqualTo(key1);
|
||||
@@ -222,7 +225,7 @@ public class HandlerMethodMappingTests {
|
||||
|
||||
this.mapping.unregisterMapping(key);
|
||||
assertThat(mapping.getHandlerInternal(new MockHttpServletRequest("GET", key))).isNull();
|
||||
assertThat(this.mapping.getMappingRegistry().getMappingsByUrl(key)).isNull();
|
||||
assertThat(this.mapping.getMappingRegistry().getMappingsByDirectPath(key)).isNull();
|
||||
assertThat(this.mapping.getMappingRegistry().getHandlerMethodsByMappingName(this.method1.getName())).isNull();
|
||||
assertThat(this.mapping.getMappingRegistry().getCorsConfiguration(handlerMethod)).isNull();
|
||||
}
|
||||
@@ -253,26 +256,30 @@ public class HandlerMethodMappingTests {
|
||||
|
||||
private PathMatcher pathMatcher = new AntPathMatcher();
|
||||
|
||||
private final List<String> matches = new ArrayList<>();
|
||||
|
||||
public MyHandlerMethodMapping() {
|
||||
setHandlerMethodMappingNamingStrategy(new SimpleMappingNamingStrategy());
|
||||
}
|
||||
|
||||
public List<String> getMatches() {
|
||||
return this.matches;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isHandler(Class<?> beanType) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMappingForMethod(Method method, Class<?> handlerType) {
|
||||
String methodName = method.getName();
|
||||
return methodName.startsWith("handler") ? methodName : null;
|
||||
protected Set<String> getDirectPaths(String mapping) {
|
||||
return (pathMatcher.isPattern(mapping) ? Collections.emptySet() : Collections.singleton(mapping));
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
protected Set<String> getMappingPathPatterns(String key) {
|
||||
return (this.pathMatcher.isPattern(key) ? Collections.<String>emptySet() : Collections.singleton(key));
|
||||
protected String getMappingForMethod(Method method, Class<?> handlerType) {
|
||||
String methodName = method.getName();
|
||||
return methodName.startsWith("handler") ? methodName : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -285,7 +292,11 @@ public class HandlerMethodMappingTests {
|
||||
@Override
|
||||
protected String getMatchingMapping(String pattern, HttpServletRequest request) {
|
||||
String lookupPath = this.pathHelper.getLookupPathForRequest(request);
|
||||
return this.pathMatcher.match(pattern, lookupPath) ? pattern : null;
|
||||
String match = (this.pathMatcher.match(pattern, lookupPath) ? pattern : null);
|
||||
if (match != null) {
|
||||
this.matches.add(match);
|
||||
}
|
||||
return match;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user