spring路径匹配,在spring5以后,有了AntPathMatcher 和 PathPattern 两种方式。
PathPattern是在spring 5 以后新增的。本文就介绍 AntPathMatcher 和 PathPattern 的异同,以及这两者的使用场景和方式。
创建实例
AntPathMatcher在1.x版本就存在了,在spring5以前,一直作为spring的路径匹配器存在。
AntPathMatcher在spring中是作为工具类存在的,所以直接new
对象即可。
PathPattern出现在spring5.x版本中, 旨在用于替换掉较为“古老”的AntPathMatcher。
PathPattern是spring一个内部类,不能通过public的方式来创建。不过spring 官方提供了一个parser
开提供PathPattern的默认实例。
// 构建AntPathMatcher 实例
AntPathMatcher antPathMatcher = new AntPathMatcher();
// 构建PathPattern 实例
PathPatternParser patternParser = PathPatternParser.defaultInstance;
匹配规则
AntPathMatcher 官方文档:
映射使用以下规则匹配 URL:
? 匹配一个字符
* 匹配零个或多个字符
** 匹配路径中的零 个 或多个目录
{spring:[a-z]+} 将正则表达 [a-z]+ 式匹配为名为“spring”的路径变量
例子
com/t?st.jsp — 匹配 com/test.jsp ,但也 com/tast.jsp 匹配或 com/txst.jsp
com/*.jsp— 匹配目录中com的所有.jsp文件
com/**/test.jsp— 匹配路径下com的所有test.jsp文件
org/springframework/**/*.jsp— 匹配路径下org/springframework的所有.jsp文件
org/**/servlet/bla.jsp — 匹配 org/springframework/servlet/bla.jsp ,但也 org/springframework/testing/servlet/bla.jsp 和 org/servlet/bla.jsp
com/{filename:\\w+}.jsp将匹配com/test.jsp该值testfilename并将其分配给变量
注意: 模式和路径必须都是绝对的,或者必须都是相对的,才能使两者匹配。因此,建议此实现的用户清理模式,以便在使用模式的上下文中为“/”作为前缀。
PathPattern 官方文档
PathPattern 使用以下规则匹配 URL 路径:
? 匹配一个字符
* 匹配路径段中的零个或多个字符
** 匹配零个或多个 路径段 ,直到路径结束
{spring} 匹配 路径段 并将其捕获为名为“spring”的变量
{spring:[a-z]+} 将正则表达 [a-z]+ 式匹配为名为“spring”的路径变量
{*spring} 匹配零个或多个 路径段 ,直到路径的末尾,并将其捕获为名为“spring”的变量
注意: 与 org.springframework.util.AntPathMatcher相反, ** 仅在模式的末尾受支持。例如 /pages/{**} ,是有效的,但不是 /pages/{**}/details 。这同样适用于捕获变体 {*spring}。目的是在比较模式的特异性时消除歧义。
例子
/pages/t?st.html — 匹配 /pages/test.html , /pages/tXst.html 但不是 /pages/toast.html
/resources/*.png— 匹配目录中resources的所有.png文件
/resources/** — 匹配路径下 /resources/ 的所有文件,包括 /resources/image.png 和 /resources/css/spring.css
/resources/{*path} — 匹配 和 下的所有文件 /resources/, /resources并在名为 “path” 的变量中捕获它们的相对路径; /resources/image.png 将与 “path” 匹配 → “/image.png”,并将 /resources/css/spring.css 与 “path” 匹配 → “/css/spring.css”
/resources/{filename:\\w+}.dat将匹配/resources/spring.dat该值"spring"filename并将其分配给变量
可见,AntPathMatcher 和 PathPattern 的匹配规则是差不多的,都支持?
*
**
已经部分正则匹配。
但PathPattern增加了路径匹配并提取结果的能力,同时为了消除歧义,不再支持路径中间的**
匹配,其他一样。
demo:
*匹配
public class TestPathMatch {
public static void main(String[] args) {
AntPathMatcher antPathMatcher = new AntPathMatcher();
PathPatternParser patternParser = PathPatternParser.defaultInstance;
List<String> list = List.of("aaa_1.csv", "aaa_2.csv", "aaa_3.csv", "aaa_1.xlsx", "aaa", "aaa.pdf");
String pattern1 = "a*.csv";
System.out.println("by antPathMatcher..");
for (String s : list) {
System.out.printf("pattern %s match %s is %s %n", pattern1,s, antPathMatcher.match(pattern1,s));
}
System.out.println("by patternParser..");
for (String s : list) {
System.out.printf("pattern %s match %s is %s %n", pattern1,s, patternParser.parse(pattern1).matches(PathContainer.parsePath(s)));
}
}
}
// 结果
// by antPathMatcher..
// pattern a*.csv match aaa_1.csv is true
// pattern a*.csv match aaa_2.csv is true
// pattern a*.csv match aaa_3.csv is true
// pattern a*.csv match aaa_1.xlsx is false
// pattern a*.csv match aaa is false
// pattern a*.csv match aaa.pdf is false
// by patternParser..
// pattern a*.csv match aaa_1.csv is true
// pattern a*.csv match aaa_2.csv is true
// pattern a*.csv match aaa_3.csv is true
// pattern a*.csv match aaa_1.xlsx is false
// pattern a*.csv match aaa is false
// pattern a*.csv match aaa.pdf is false
PathPattern 路径提取
public class TestPathMatch {
public static void main(String[] args) {
AntPathMatcher antPathMatcher = new AntPathMatcher();
PathPatternParser patternParser = PathPatternParser.defaultInstance;
String pattern = "/api/hancher/{*spring}";
List<String> list = List.of("/api/hancher/a/b/c", "/api/hancher/a", "/api/hancher/a/", "/api/hancher/a/b/c.csv","/api/test/a/b/c");
for (String s : list) {
System.out.printf("pattern %s match %s is %s %n", pattern,s, patternParser.parse(pattern).matches(PathContainer.parsePath(s)));
System.out.printf("pattern %s match %s result %s %n", pattern,s, patternParser.parse(pattern).matchAndExtract(PathContainer.parsePath(s)));
}
}
}
// 结果
//pattern /api/hancher/{*spring} match /api/hancher/a/b/c is true
//pattern /api/hancher/{*spring} match /api/hancher/a/b/c result PathMatchInfo[uriVariables={spring=/a/b/c}, matrixVariables={}]
//pattern /api/hancher/{*spring} match /api/hancher/a is true
//pattern /api/hancher/{*spring} match /api/hancher/a result PathMatchInfo[uriVariables={spring=/a}, matrixVariables={}]
//pattern /api/hancher/{*spring} match /api/hancher/a/ is true
//pattern /api/hancher/{*spring} match /api/hancher/a/ result PathMatchInfo[uriVariables={spring=/a/}, matrixVariables={}]
//pattern /api/hancher/{*spring} match /api/hancher/a/b/c.csv is true
//pattern /api/hancher/{*spring} match /api/hancher/a/b/c.csv result PathMatchInfo[uriVariables={spring=/a/b/c.csv}, matrixVariables={}]
//pattern /api/hancher/{*spring} match /api/test/a/b/c is false
//pattern /api/hancher/{*spring} match /api/test/a/b/c result null
性能
官方说PathPattern 比 AntPathMatcher 更快一些,所以咱们验证一下:
测试代码
public class TestPathMatch {
public static void main(String[] args) {
AntPathMatcher antPathMatcher = new AntPathMatcher();
PathPatternParser patternParser = PathPatternParser.defaultInstance;
long start = System.currentTimeMillis();
String pattern = "/api/hanc?er/**";
for (int i = 0; i < 100000; i++) {
String path = "/api/hancher/" + i + "/" + i + "/a.txt";
System.out.println(antPathMatcher.match(pattern, path));
// System.out.println(patternParser.parse(pattern).matches(PathContainer.parsePath(path)));
}
System.out.println(System.currentTimeMillis() - start);
}
}
分别跑了5次。
AntPathMatcher 平均耗时:392ms
PathPattern 平均耗时:323.4ms
官方所言不虚!
选择
- 在满足需求的情况下,尽量选PathPattern,更快
- 如果需要通过*匹配中间路径,或者通过匹配文件扩展名这种场景,用AntPathMatcher, 因为PathPattern不支持。
文档信息
- 本文作者:寒澈
- 本文链接:https://www.hancher.top/2024/05/21/spring-path-matcher/
- 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)