AntPathMatcher VS PathPattern

2024/05/21 spring 共 5262 字,约 16 分钟

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
官方所言不虚!

选择

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

文档信息

Search

    Table of Contents