在 Spring Boot (特别是 Spring MVC) 中,当一个请求(例如 /user/1)到达时,框架需要决定由哪个 Controller 的哪个方法来处理。这个过程就叫做路径匹配。
从 Spring Boot 2.6.0 开始,路径匹配策略发生了重大变化。目前主要支持以下两种风格:
- AntPathMatcher (经典/传统风格)
- PathPatternParser (现代/高性能风格)
下面详细介绍这两种风格的区别、语法以及如何配置。
1. AntPathMatcher (经典风格)
这是 Spring 早期一直使用的默认匹配器,基于 Apache Ant 的文件路径匹配规则。它的特点是非常灵活,但在处理复杂 URL 时性能稍弱。
核心语法
?:匹配 1 个 字符。/user/get?-> 匹配/user/get1,/user/getA
*:匹配 0 个或多个 字符(仅限当前层级目录)。/user/*-> 匹配/user/1,/user/abc- 不匹配
/user/1/details(跨层级了)
**:匹配 0 个或多个 目录(支持多层级)。/user/**-> 匹配/user/1,/user/1/details/edit
特点
- 灵活性极高 :
**可以放在路径的中间 。- 例如:
/api/**/details可以匹配/api/user/details和/api/order/v1/details。
- 例如:
- 性能:基于字符串操作,每次匹配都需要重新计算,对于高并发下的长 URL,性能略逊于新版。
- 现状:在 Spring Boot 2.6 之前是默认值。
2. PathPatternParser (现代风格)
这是 Spring 5.3 引入,并成为 Spring Boot 2.6+ 的默认配置。它是专门为 Web URL 设计的,而不是照搬文件路径规则。
核心语法
基本兼容 AntPathMatcher,但在通配符位置上有严格限制:
?和*:用法同上。**:只能放在路径的末尾 。- 合法 :
/user/** - 非法 :
/user/**/details(启动时会直接报错!)
- 合法 :
{*pathVariable}:支持更强大的路径变量捕获(这是新特性)。/resources/{*path}-> 可以匹配/resources/img/icon.png,并且把img/icon.png整个捕获给变量path。
特点
- 高性能 :它在应用启动时就把 URL 解析成了一棵语法树 (AST),匹配请求时是基于树节点的匹配,而不是字符串正则,速度非常快。
- 更严格 :禁止在路径中间使用
**,这消除了很多歧义匹配的问题。 - 现状 :Spring Boot 2.6 及以后版本的默认值。
3. 两种风格对比
| 特性 | AntPathMatcher | PathPatternParser |
|---|---|---|
| 引入时间 | Spring 很早就有了 | Spring 5.3+ |
| Spring Boot 默认 | < 2.6 版本 | ≥ 2.6 版本 |
** 的位置 |
可以放在中间 (如 /a/**/b) |
只能放在末尾 |
| 匹配算法 | 字符串操作/正则 | 预解析语法树 (AST) |
| 性能 | 一般 | 极高 |
| 路径变量 | 支持 {id} |
支持 {id} 和 {*path} (捕获剩余路径) |
| 兼容性问题 | 兼容性极好(老项目多用它) | 某些老旧库(如老版 Swagger/Springfox)可能不支持 |
4. 如何配置切换?
如果你升级了 Spring Boot 到 2.6+,发现以前写的 /api/**/xxx 接口报错了,或者集成的 Swagger 文档挂了,你可以通过修改 application.yml 来切换回老模式。
配置属性: spring.mvc.pathmatch.matching-strategy
使用 PathPatternParser (默认,推荐)
yaml
spring:
mvc:
pathmatch:
matching-strategy: path_pattern_parser
回退到 AntPathMatcher (老模式)
yaml
spring:
mvc:
pathmatch:
matching-strategy: ant_path_matcher
5. 总结
- 新项目 :强烈建议使用默认的 PathPatternParser。它的性能更好,且"禁止中间双星号"的限制其实有助于保持 API 设计的整洁。
- 老项目迁移 :如果你的老代码里大量使用了
/a/**/b这种中间通配符,或者使用了不兼容 PathPatternParser 的第三方库(如旧版 Springfox Swagger),为了快速迁移,可以将策略改回ant_path_matcher。 - 捕获剩余路径 :如果你需要实现类似"网关"的功能,把某个前缀后的所有路径都截取下来,PathPatternParser 的
/{*path}语法非常方便。