在 Spring MVC 中使用 PathPattern 进行 URL 匹配

工程 | Rossen Stoyanchev | 2020年6月30日 | ...

最近发布的 Spring Framework 5.3 M1 公告中提到了“Spring MVC 提供了 PathPattern 解析,可实现高效的 URL 匹配”。本文将提供更多背景和细节来扩展这一点。

概述

在 Spring 应用中,AntPathMatcher 用于识别 Spring 配置中的类路径、文件系统、远程和其他资源。它也曾用于 Spring MVC 匹配 URL 路径。随着 Web 应用中模式的使用数量和语法的增长,AntPathMatcher 不断演进以满足这些需求,但一些痛点仍然存在且未得到解决。

  1. 在 Web 应用中,每个请求可能需要匹配多次模式,因此任何性能和效率的提升都很重要。然而,String 模式匹配限制了所能实现的功能。

  2. 多年来,从多个匹配请求的模式中选择最具体的模式一直是一个挑战,没有简单的方法可以使其更可预测而不影响其他情况。

  3. String 路径匹配到 String 模式会很难避免 URI 编码问题。例如,是应该先解码传入的路径然后进行匹配?这允许模式本身在声明时不包含编码字符,但如果请求路径包含 %2F%3B(分别代表 /;)怎么办?解码后,它们会改变路径的结构,使其更难可靠地匹配。我们可以通过 UrlPathHelper#urlDecode 将请求路径保持编码状态,但这样就不能使用前缀 Servlet 映射,因为 servletPath 本身已被解码,并且我们的模式也需要被编码。

  4. 路径参数也带来了类似的挑战。它们可以在匹配前移除,但如果我们想通过 @MatrixVariable 提取它们怎么办?我们可以通过 UrlPathHelper#removeSemicolonContent 将它们保留在路径中,但现在模式必须考虑路径参数。

PathPattern

Spring Framework 5.0 中引入 Spring WebFlux 是重新思考这一切并创建替代方案的好机会。这促成了创建解析后的 PathPattern,并将其与表示 URL 路径的解析后的 PathContainer 进行匹配。

模式在启动时进行解析,并在运行时重用以实现高效的 URL 匹配。效率有多高?在没有具体用例的情况下很难给出数字,但我们的 jmh benchmark 显示吞吐量提高了 6-8 倍,分配率降低了 30-40%。您可以根据您的应用程序量身定制基准测试以获得更准确的数字。

PathPatternAntPathMatcher 语法兼容,但以下几点除外:

  1. 支持附加语法来匹配和捕获末尾的 0 个或多个路径段,例如 "/foo/{*spring}"。这在 REST API 中非常有用,可以作为一种通配符模式,并通过 @PathVariable 访问捕获的路径段。

  2. 仅允许在模式末尾使用 "**" 进行多段匹配。这有助于消除在选择最接近给定请求的匹配项时的大部分歧义。

PathContainer 有助于解决剩余的问题。例如,它从不解码整个路径,而是将其分解并单独解码路径段,同时移除路径参数,然后将解码和规范化后的值逐个进行匹配。因此,编码的 "/"";" 不会改变路径结构,并且路径参数仍然可用。这意味着无需配置如何解析请求路径,也没有需要考虑的权衡。

Spring MVC 和 PathPattern

从 Spring Framework 5.3 开始,Spring MVC 支持使用 PathPattern,所有 HandlerMapping 实现都公开了一个属性来设置 PathPatternParser,作为使用 AntPathMatcher 的替代方案。启用此功能的最简单方法是在 MVC 配置的 路径匹配选项中配置 PathPatternParser

反过来,如果 DispatcherServlet 检测到任何启用了已解析模式的 HandlerMapping,它将在运行时解析 URL 路径,并将其作为请求属性提供。同样,也可以使用 ServletRequestPathFilter 更早地完成此操作,在这种情况下,DispatcherServlet 将避免再次解析它。

PathPattern 和 AntPathMatcher 的混合使用

在某些情况下,可能会有一个 HandlerMapping 启用了已解析模式,而另一个使用了 AntPathMatcher。例如,第三方库可能会注册其自己的 HandlerMapping bean,但未启用已解析模式。虽然每个 HandlerMapping 都独立进行匹配,但其他组件(如拦截器)需要能够支持和使用已解析的 PathPatternRequestPathAntPathMatcher 的 String lookupPath,具体取决于哪一个可用。

这就是为什么从 5.3 开始,此类组件会使用 ServletRequestPathUtils 来检查可用性,并相应地使用 PathPatternAntPathMatcher。大多数情况下,应用程序不需要担心这一点,并且模式语法在很大程度上是相同的,因此应该可以正常工作。

后缀模式匹配

另外,在 5.3 版本中,Spring MVC 中使用后缀模式匹配以及其他通过路径扩展进行内容协商的选项已默认关闭。多年来,这在许多方面被证明是存在问题的。这就是为什么在使用 PathPatternParser 时不支持这一点的原因。即使使用 5.3 的 AntPathMatcher,如果您想继续使用这些选项,也需要重新启用它们。

总而言之,展望未来,我们期望 Spring MVC 应用程序切换到使用 PathPattern 而不是 AntPathMatcher,以利用效率提升、改进的语法以及更可预测的 URL 路径处理方式。请尝试使用 M1 版本,运行您的应用程序,或许可以进行基准测试,并告知我们您的反馈。

获取 Spring 新闻通讯

通过 Spring 新闻通讯保持联系

订阅

领先一步

VMware 提供培训和认证,助您加速进步。

了解更多

获得支持

Tanzu Spring 提供 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件,只需一份简单的订阅。

了解更多

即将举行的活动

查看 Spring 社区所有即将举行的活动。

查看所有