服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - Java教程 - 使用Spring AntPathMatcher的doMatch方法

使用Spring AntPathMatcher的doMatch方法

2021-12-23 13:08王其狗勾儿痒 Java教程

这篇文章主要介绍了使用Spring AntPathMatcher的doMatch方法,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

AntPathMatcher的doMatch方法

AntPathMatcher.doMatch(...), 是解决模式匹配的源码

有4个步骤

1. 分解模式字符串, 分解路径字符串

2. 第一个while 循环, 用来判断绝对匹配 /xxx/abc ==> /xxx/abc

3. 第二个while循环两个字符串数组都从最后的下标开始匹配, 直到遇到pattDir为'**'时结束

4. 第三个while循环, 主要解决有多个'**'字符串./**/djdjdjd/**, /a/**/**/b/**/c/**/*.class等

?
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
// 解决模式匹配的函数, 返回true or false 表示是否匹配
// 参数 pattern: 表示模式字符串
        path: 文件的路径
    protected boolean doMatch(String pattern, String path, boolean fullMatch, Map<String, String> uriTemplateVariables) {    
        if (path.startsWith(this.pathSeparator) != pattern.startsWith(this.pathSeparator)) {
            return false;
        }
 
        1.1. 分解模式字符串
        String[] pattDirs = tokenizePattern(pattern);
        if (fullMatch && this.caseSensitive && !isPotentialMatch(path, pattDirs)) {
            return false;
        }
        
        1.2 分解路径字符串
        String[] pathDirs = tokenizePath(path);    
        // pattern的可分配下标 pattIdxStart ~ pattIdxEnd
        // path的可分配下标   pathIdxStart ~ pathIdxEnd
        int pattIdxStart = 0;
        int pattIdxEnd = pattDirs.length - 1;
        int pathIdxStart = 0;
        int pathIdxEnd = pathDirs.length - 1;
 
        // Match all elements up to the first **
        // 2. 第一个while 循环, 用来判断绝对匹配的   /xxx/abc ==> /xxx/abc
        // 两个字符串都从下标0开始, 直到模式字符串遇到**结束
        while (pattIdxStart <= pattIdxEnd && pathIdxStart <= pathIdxEnd) {
            String pattDir = pattDirs[pattIdxStart];
            if ("**".equals(pattDir)) {
                break;
            }
            if (!matchStrings(pattDir, pathDirs[pathIdxStart], uriTemplateVariables)) {
                return false;
            }
            pattIdxStart++;
            pathIdxStart++;
        }
 
        // pathIdxStart > pathIdEnd, 表示文件路径(path), 已经逐一的匹配到了
        if (pathIdxStart > pathIdxEnd) {
            
            /*
            // Path is exhausted, only match if rest of pattern is * or **'s
            if (pattIdxStart > pattIdxEnd) {
                // 判断最后一个字符是否为'/'
                return (pattern.endsWith(this.pathSeparator) == path.endsWith(this.pathSeparator));
            }
            if (!fullMatch) {
                return true;
            }
            if (pattIdxStart == pattIdxEnd && pattDirs[pattIdxStart].equals("*") && path.endsWith(this.pathSeparator)) {
                return true;
            }
            // 不会执行到这里
            for (int i = pattIdxStart; i <= pattIdxEnd; i++) {
                if (!pattDirs[i].equals("**")) {
                    return false;
                }
            }
            */
            // 这里返回true 一般是相等的字符串匹配(长度相同)
            // /abc/zzzz ==> /abc/zzzz
            return true;
        }
        
        /*
        else if (pattIdxStart > pattIdxEnd) {
            // String not exhausted, but pattern is. Failure.
            return false;
        }
        else if (!fullMatch && "**".equals(pattDirs[pattIdxStart])) {
            // Path start definitely matches due to "**" part in pattern.
            return true;
        }*/
        
        // 3. 两个字符串数组都从最后的下标开始匹配, 直到遇到pattDir为'**'时结束
        while (pattIdxStart <= pattIdxEnd && pathIdxStart <= pathIdxEnd) {
            String pattDir = pattDirs[pattIdxEnd];
            if (pattDir.equals("**")) {
                break;
            }
            if (!matchStrings(pattDir, pathDirs[pathIdxEnd], uriTemplateVariables)) {
                return false;
            }
            pattIdxEnd--;
            pathIdxEnd--;
        }
        if (pathIdxStart > pathIdxEnd) {
            for (int i = pattIdxStart; i <= pattIdxEnd; i++) {
                if (!pattDirs[i].equals("**")) {
                    return false;
                }
            }
            // 这里返回true 一般字符串为
            // /xxxx/abcd/**/*.class => /xxxx/abcd /xxx.class
            // 即只有一个**, 而且**没发挥到什么作用
            // 测试
            // AntPathMatcher ant = new AntPathMatcher("/");
            //String pattern = "/abc/**/*.class";
            //String path = "/abc/ddd.class";
            //System.out.println(ant.match(pattern, path));
            return true;
        }
 
        // 4. 第3个while循环, 主要解决有多个'**'字符串.   /**/djdjdjd/**, /a/**/**/b/**/c/**/*.class等
        // 每次下标又从pattIdxStart+1开始
        while (pattIdxStart != pattIdxEnd && pathIdxStart <= pathIdxEnd) {
            int patIdxTmp = -1; // 这个用来指向**的位置
            for (int i = pattIdxStart + 1; i <= pattIdxEnd; i++) {
                if (pattDirs[i].equals("**")) {
                    patIdxTmp = i;
                    break;
                }
            }
            if (patIdxTmp == pattIdxStart + 1) {
                // '**/**' 遇到连续的/**/**就跳过, 因为这没有意义, 一个/**也可以表达多条路径
                pattIdxStart++;
                continue;
            }
            // patLength: 两个'**'之间的字符串的长度  /**/a/b/** = 2
            // strLength: 路径还剩多少个没匹配    /a/b/c/d    如果/a/b都匹配了, 就只剩下/b/c = 2
            int patLength = (patIdxTmp - pattIdxStart - 1);
            int strLength = (pathIdxEnd - pathIdxStart + 1);
            int foundIdx = -1;
 
            strLoop:
            // 因为已经确定了有 /**/a/b/**这样的模式字符串存在, 中间2长度
            // 如果存在/q/a/b/c/d 有5个长度, 那么就要循环3次
            // 第一次匹配 /a/b => /q/a
            // 第二次   /a/b => /a/b    => 这里已经匹配到了, 所以就break了
            //           /a/b => /b/c
            //           /a/b => /c/d
            // 当然, 如果存在更复杂的如: /**/a/b/**/a/b/**/a/b/**, 外层的while循环就会做3次判断,
            //String pattern = "/**/a/b/**/a/b/**/a/b/**";
            //String path = "/q/q/q/a/b/q/q/q/a/b/q/q/q/a/b/q/q/q/a/b";
            for (int i = 0; i <= strLength - patLength; i++) {
                for (int j = 0; j < patLength; j++) {
                    String subPat = pattDirs[pattIdxStart + j + 1];
                    String subStr = pathDirs[pathIdxStart + i + j];
                    if (!matchStrings(subPat, subStr, uriTemplateVariables)) {
                        continue strLoop;
                    }
                }
                foundIdx = pathIdxStart + i;
                break;
            }
 
            if (foundIdx == -1) {
                return false;
            }
 
            pattIdxStart = patIdxTmp;
            pathIdxStart = foundIdx + patLength;
        }
 
        for (int i = pattIdxStart; i <= pattIdxEnd; i++) {
            if (!pattDirs[i].equals("**")) {
                return false;
            }
        }
        //如果上面的都没有返回值 /** => /sdjdd/djkd/.....就会在此处返回
        // 当然还有更多的
        return true;
    }

Spring的AntPathMatcher工具类用法

AntPathMatcher

是org.springframework.util工具包下的方法。

?
1
2
3
4
5
6
7
8
9
10
/**
 * A convenient, alternative constructor to use with a custom path separator.
 * @param pathSeparator the path separator to use, must not be {@code null}.
 * @since 4.1
 */
public AntPathMatcher(String pathSeparator) {
 Assert.notNull(pathSeparator, "'pathSeparator' is required");
 this.pathSeparator = pathSeparator;
 this.pathSeparatorPatternCache = new PathSeparatorPatternCache(pathSeparator);
}
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public boolean hasUrl(String url) {
    if (url == null || "".equals(url)) {
    return false;
}
 
AntPathMatcher antPathMatcher = new AntPathMatcher();
// 可根据需求做动态匹配
String pattern = "/app/*.html"
if (antPathMatcher.match(pattern, url)) {
   // 根据入参url和pattern匹配上返回ture,否则false.
     return true;
 }
   return false;
}

下面是模糊匹配规则

也就是在响应的路径上添加* 或则 ** 对路径进行替代即可。

URL路径 说明
/app/*.x 匹配(Matches)所有在app路径下的.x文件
/app/p?ttern 匹配(Matches) /app/pattern 和 /app/pXttern,但是不包括/app/pttern
/**/example 匹配(Matches) /app/example, /app/foo/example, 和 /example
/app/**/dir/file. 匹配(Matches) /app/dir/file.jsp, /app/foo/dir/file.html,/app/foo/bar/dir/file.pdf, 和 /app/dir/file.java
/**/*.jsp 匹配(Matches)任何的.jsp 文件

以上为个人经验,希望能给大家一个参考,也希望大家多多支持服务器之家。

原文链接:https://blog.csdn.net/qq_38598257/article/details/88878533

延伸 · 阅读

精彩推荐
  • Java教程升级IDEA后Lombok不能使用的解决方法

    升级IDEA后Lombok不能使用的解决方法

    最近看到提示IDEA提示升级,寻思已经有好久没有升过级了。升级完毕重启之后,突然发现好多错误,本文就来介绍一下如何解决,感兴趣的可以了解一下...

    程序猿DD9332021-10-08
  • Java教程Java实现抢红包功能

    Java实现抢红包功能

    这篇文章主要为大家详细介绍了Java实现抢红包功能,采用多线程模拟多人同时抢红包,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙...

    littleschemer13532021-05-16
  • Java教程20个非常实用的Java程序代码片段

    20个非常实用的Java程序代码片段

    这篇文章主要为大家分享了20个非常实用的Java程序片段,对java开发项目有所帮助,感兴趣的小伙伴们可以参考一下 ...

    lijiao5352020-04-06
  • Java教程xml与Java对象的转换详解

    xml与Java对象的转换详解

    这篇文章主要介绍了xml与Java对象的转换详解的相关资料,需要的朋友可以参考下...

    Java教程网2942020-09-17
  • Java教程小米推送Java代码

    小米推送Java代码

    今天小编就为大家分享一篇关于小米推送Java代码,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧...

    富贵稳中求8032021-07-12
  • Java教程Java BufferWriter写文件写不进去或缺失数据的解决

    Java BufferWriter写文件写不进去或缺失数据的解决

    这篇文章主要介绍了Java BufferWriter写文件写不进去或缺失数据的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望...

    spcoder14552021-10-18
  • Java教程Java使用SAX解析xml的示例

    Java使用SAX解析xml的示例

    这篇文章主要介绍了Java使用SAX解析xml的示例,帮助大家更好的理解和学习使用Java,感兴趣的朋友可以了解下...

    大行者10067412021-08-30
  • Java教程Java8中Stream使用的一个注意事项

    Java8中Stream使用的一个注意事项

    最近在工作中发现了对于集合操作转换的神器,java8新特性 stream,但在使用中遇到了一个非常重要的注意点,所以这篇文章主要给大家介绍了关于Java8中S...

    阿杜7482021-02-04