AntPathMatcher VS PathPattern

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的默认实例。

1
2
3
4
// 构建AntPathMatcher 实例
AntPathMatcher antPathMatcher = new AntPathMatcher();
// 构建PathPattern 实例
PathPatternParser patternParser = PathPatternParser.defaultInstance;

匹配规则

AntPathMatcher 官方文档:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
映射使用以下规则匹配 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 官方文档

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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:

*匹配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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 路径提取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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 更快一些,所以咱们验证一下:

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
官方所言不虚!

选择

  1. 在满足需求的情况下,尽量选PathPattern,更快
  2. 如果需要通过*匹配中间路径,或者通过匹配文件扩展名这种场景,用AntPathMatcher, 因为PathPattern不支持。

AntPathMatcher VS PathPattern
https://www.hancher.top/2023/05/21/spring-spring-path-matcher/
作者
寒澈
发布于
2023年5月21日
许可协议