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

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

最近发布的 Spring Framework 5.3 M1 公告 提到“Spring MVC 带有 PathPattern 解析,用于高效的 URL 匹配”。本文将对此进行更深入的介绍。

概述

在 Spring 应用程序中,AntPathMatcher 用于识别 Spring 配置中的 classpath、文件系统、远程资源以及其他资源。它也曾用于 Spring MVC 中匹配 URL 路径。随着时间的推移,Web 应用程序中模式(patterns)的使用在数量和语法上不断增长,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 进行匹配。

模式(Patterns)在启动时被解析,并在运行时重复使用以进行高效的 URL 匹配。效率提高了多少?很难在没有具体用例的情况下给出确切数字,但我们的 jmh 基准测试 显示吞吐量提高了 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 独立进行自己的匹配,但拦截器等其他组件需要能够支持和使用解析后的 RequestPath(配合 PathPattern)或 String lookupPath(配合 AntPathMatcher),具体取决于哪个可用。

因此,从 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 社区中即将到来的所有活动。

查看全部