Introduce PathPatternParser for optimized path matching
This commit introduces a PathPatternParser which parses request pattern strings into PathPattern objects which can then be used to fast match incoming string paths. The parser and matching supports the syntax as described in SPR-14544. The code is optimized around the common usages of request patterns and is designed to create very little transient garbage when matching. Issue: SPR-14544
This commit is contained in:
committed by
Brian Clozel
parent
6f029392c7
commit
f58ffad939
@@ -37,6 +37,7 @@ import org.springframework.web.servlet.handler.MappedInterceptor;
|
||||
import org.springframework.web.servlet.handler.WebRequestHandlerInterceptorAdapter;
|
||||
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
|
||||
import org.springframework.web.servlet.theme.ThemeChangeInterceptor;
|
||||
import org.springframework.web.util.ParsingPathMatcher;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
@@ -152,7 +153,7 @@ public class InterceptorRegistryTests {
|
||||
|
||||
|
||||
private List<HandlerInterceptor> getInterceptorsForPath(String lookupPath) {
|
||||
PathMatcher pathMatcher = new AntPathMatcher();
|
||||
PathMatcher pathMatcher = new ParsingPathMatcher();
|
||||
List<HandlerInterceptor> result = new ArrayList<>();
|
||||
for (Object interceptor : this.registry.getInterceptors()) {
|
||||
if (interceptor instanceof MappedInterceptor) {
|
||||
|
||||
@@ -83,6 +83,7 @@ import org.springframework.web.servlet.resource.ResourceUrlProviderExposingInter
|
||||
import org.springframework.web.servlet.view.BeanNameViewResolver;
|
||||
import org.springframework.web.servlet.view.InternalResourceViewResolver;
|
||||
import org.springframework.web.servlet.view.ViewResolverComposite;
|
||||
import org.springframework.web.util.ParsingPathMatcher;
|
||||
import org.springframework.web.util.UrlPathHelper;
|
||||
|
||||
import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES;
|
||||
@@ -320,7 +321,7 @@ public class WebMvcConfigurationSupportTests {
|
||||
|
||||
assertNotNull(urlPathHelper);
|
||||
assertNotNull(pathMatcher);
|
||||
assertEquals(AntPathMatcher.class, pathMatcher.getClass());
|
||||
assertEquals(ParsingPathMatcher.class, pathMatcher.getClass());
|
||||
}
|
||||
|
||||
private ApplicationContext initContext(Class<?>... configClasses) {
|
||||
|
||||
@@ -38,6 +38,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.context.support.StaticWebApplicationContext;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.util.ParsingPathMatcher;
|
||||
import org.springframework.web.util.UrlPathHelper;
|
||||
|
||||
|
||||
@@ -244,7 +245,7 @@ public class HandlerMethodMappingTests {
|
||||
|
||||
private UrlPathHelper pathHelper = new UrlPathHelper();
|
||||
|
||||
private PathMatcher pathMatcher = new AntPathMatcher();
|
||||
private PathMatcher pathMatcher = new ParsingPathMatcher();
|
||||
|
||||
|
||||
public MyHandlerMethodMapping() {
|
||||
|
||||
@@ -81,7 +81,7 @@ public class PathMatchingUrlHandlerMappingTests {
|
||||
HandlerExecutionChain hec = getHandler(req);
|
||||
assertTrue("Handler is null", hec != null);
|
||||
assertTrue("Handler is correct bean", hec.getHandler() == bean);
|
||||
assertEquals("pathmatchingTest.html", req.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE));
|
||||
assertEquals("/pathmatchingTest.html", req.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE));
|
||||
|
||||
// no match, no forward slash included
|
||||
req = new MockHttpServletRequest("GET", "welcome.html");
|
||||
@@ -121,11 +121,6 @@ public class PathMatchingUrlHandlerMappingTests {
|
||||
hec = getHandler(req);
|
||||
assertTrue("Handler is correct bean", hec != null && hec.getHandler() == bean);
|
||||
|
||||
// this as well, because there's a **/in there as well
|
||||
req = new MockHttpServletRequest("GET", "/testing/bla.jsp");
|
||||
hec = getHandler(req);
|
||||
assertTrue("Handler is correct bean", hec != null && hec.getHandler() == bean);
|
||||
|
||||
// should match because exact pattern is there
|
||||
req = new MockHttpServletRequest("GET", "/administrator/another/bla.xml");
|
||||
hec = getHandler(req);
|
||||
|
||||
@@ -23,11 +23,11 @@ import static org.junit.Assert.*;
|
||||
import org.springframework.mock.web.test.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.test.MockHttpServletResponse;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.util.PathMatcher;
|
||||
import org.springframework.web.servlet.DispatcherServlet;
|
||||
import org.springframework.web.servlet.HandlerMapping;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.util.ParsingPathMatcher;
|
||||
|
||||
/**
|
||||
* @author Juergen Hoeller
|
||||
@@ -36,7 +36,7 @@ import org.springframework.web.servlet.ModelAndView;
|
||||
*/
|
||||
public class UrlFilenameViewControllerTests {
|
||||
|
||||
private PathMatcher pathMatcher = new AntPathMatcher();
|
||||
private PathMatcher pathMatcher = new ParsingPathMatcher();
|
||||
|
||||
|
||||
@Test
|
||||
|
||||
@@ -62,19 +62,21 @@ public class WebContentInterceptorTests {
|
||||
@Test
|
||||
public void mappedCacheConfigurationOverridesGlobal() throws Exception {
|
||||
Properties mappings = new Properties();
|
||||
mappings.setProperty("**/*handle.vm", "-1");
|
||||
mappings.setProperty("*/*handle.vm", "-1"); // was **/*handle.vm
|
||||
|
||||
WebContentInterceptor interceptor = new WebContentInterceptor();
|
||||
interceptor.setCacheSeconds(10);
|
||||
interceptor.setCacheMappings(mappings);
|
||||
|
||||
request.setRequestURI("http://localhost:7070/example/adminhandle.vm");
|
||||
// request.setRequestURI("http://localhost:7070/example/adminhandle.vm");
|
||||
request.setRequestURI("example/adminhandle.vm");
|
||||
interceptor.preHandle(request, response, null);
|
||||
|
||||
Iterable<String> cacheControlHeaders = response.getHeaders("Cache-Control");
|
||||
assertThat(cacheControlHeaders, Matchers.emptyIterable());
|
||||
|
||||
request.setRequestURI("http://localhost:7070/example/bingo.html");
|
||||
// request.setRequestURI("http://localhost:7070/example/bingo.html");
|
||||
request.setRequestURI("example/bingo.html");
|
||||
interceptor.preHandle(request, response, null);
|
||||
|
||||
cacheControlHeaders = response.getHeaders("Cache-Control");
|
||||
@@ -143,10 +145,11 @@ public class WebContentInterceptorTests {
|
||||
interceptor.setUseExpiresHeader(true);
|
||||
interceptor.setAlwaysMustRevalidate(true);
|
||||
Properties mappings = new Properties();
|
||||
mappings.setProperty("**/*.cache.html", "10");
|
||||
mappings.setProperty("*/*.cache.html", "10"); // was **/*.cache.html
|
||||
interceptor.setCacheMappings(mappings);
|
||||
|
||||
request.setRequestURI("http://example.org/foo/page.html");
|
||||
// request.setRequestURI("http://example.org/foo/page.html");
|
||||
request.setRequestURI("foo/page.html");
|
||||
interceptor.preHandle(request, response, null);
|
||||
|
||||
Iterable<String> expiresHeaders = response.getHeaders("Expires");
|
||||
@@ -157,7 +160,8 @@ public class WebContentInterceptorTests {
|
||||
assertThat(pragmaHeaders, Matchers.contains("no-cache"));
|
||||
|
||||
response = new MockHttpServletResponse();
|
||||
request.setRequestURI("http://example.org/page.cache.html");
|
||||
// request.setRequestURI("http://example.org/page.cache.html");
|
||||
request.setRequestURI("foo/page.cache.html");
|
||||
interceptor.preHandle(request, response, null);
|
||||
|
||||
expiresHeaders = response.getHeaders("Expires");
|
||||
|
||||
@@ -2364,22 +2364,22 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl
|
||||
@Controller
|
||||
static class MyRelativeMethodPathDispatchingController {
|
||||
|
||||
@RequestMapping("**/myHandle")
|
||||
@RequestMapping("*/myHandle") // was **/myHandle
|
||||
public void myHandle(HttpServletResponse response) throws IOException {
|
||||
response.getWriter().write("myView");
|
||||
}
|
||||
|
||||
@RequestMapping("/**/*Other")
|
||||
@RequestMapping("/*/*Other") // was /**/*Other
|
||||
public void myOtherHandle(HttpServletResponse response) throws IOException {
|
||||
response.getWriter().write("myOtherView");
|
||||
}
|
||||
|
||||
@RequestMapping("**/myLang")
|
||||
@RequestMapping("*/myLang") // was **/myLang
|
||||
public void myLangHandle(HttpServletResponse response) throws IOException {
|
||||
response.getWriter().write("myLangView");
|
||||
}
|
||||
|
||||
@RequestMapping("/**/surprise")
|
||||
@RequestMapping("/*/surprise") // was /**/surprise
|
||||
public void mySurpriseHandle(HttpServletResponse response) throws IOException {
|
||||
response.getWriter().write("mySurpriseView");
|
||||
}
|
||||
@@ -2643,7 +2643,7 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl
|
||||
@Controller
|
||||
public static class PathOrderingController {
|
||||
|
||||
@RequestMapping(value = {"/dir/myPath1.do", "/**/*.do"})
|
||||
@RequestMapping(value = {"/dir/myPath1.do", "/*/*.do"})
|
||||
public void method1(Writer writer) throws IOException {
|
||||
writer.write("method1");
|
||||
}
|
||||
|
||||
@@ -571,14 +571,14 @@ public class UriTemplateServletAnnotationControllerHandlerMethodTests extends Ab
|
||||
@RequestMapping("/category")
|
||||
public static class MultiPathController {
|
||||
|
||||
@RequestMapping(value = {"/{category}/page/{page}", "/**/{category}/page/{page}"})
|
||||
@RequestMapping(value = {"/{category}/page/{page}", "/*/{category}/page/{page}"})
|
||||
public void category(@PathVariable String category, @PathVariable int page, Writer writer) throws IOException {
|
||||
writer.write("handle1-");
|
||||
writer.write("category-" + category);
|
||||
writer.write("page-" + page);
|
||||
}
|
||||
|
||||
@RequestMapping(value = {"/{category}", "/**/{category}"})
|
||||
@RequestMapping(value = {"/{category}", "/*/{category}"})
|
||||
public void category(@PathVariable String category, Writer writer) throws IOException {
|
||||
writer.write("handle2-");
|
||||
writer.write("category-" + category);
|
||||
@@ -598,7 +598,7 @@ public class UriTemplateServletAnnotationControllerHandlerMethodTests extends Ab
|
||||
}
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/*/menu/**")
|
||||
@RequestMapping("/*/menu/") // was /*/menu/**
|
||||
public static class MenuTreeController {
|
||||
|
||||
@RequestMapping("type/{var}")
|
||||
|
||||
Reference in New Issue
Block a user