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