使用Spring AntPathMatcher的doMatch方法
AntPathMatcher的doMatch方法
AntPathMatcher.doMatch(...), 是解決模式匹配的源碼
有4個步驟
1. 分解模式字符串, 分解路徑字符串
2. 第一個while 循環(huán), 用來判斷絕對匹配 /xxx/abc ==> /xxx/abc
3. 第二個while循環(huán)兩個字符串?dāng)?shù)組都從最后的下標(biāo)開始匹配, 直到遇到pattDir為'**'時結(jié)束
4. 第三個while循環(huán), 主要解決有多個'**'字符串./**/djdjdjd/**, /a/**/**/b/**/c/**/*.class等
// 解決模式匹配的函數(shù), 返回true or false 表示是否匹配
// 參數(shù) 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的可分配下標(biāo) pattIdxStart ~ pattIdxEnd
// path的可分配下標(biāo) 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 循環(huán), 用來判斷絕對匹配的 /xxx/abc ==> /xxx/abc
// 兩個字符串都從下標(biāo)0開始, 直到模式字符串遇到**結(jié)束
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), 已經(jīng)逐一的匹配到了
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;
}
// 不會執(zhí)行到這里
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. 兩個字符串?dāng)?shù)組都從最后的下標(biāo)開始匹配, 直到遇到pattDir為'**'時結(jié)束
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
// 即只有一個**, 而且**沒發(fā)揮到什么作用
// 測試
// 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循環(huán), 主要解決有多個'**'字符串. /**/djdjdjd/**, /a/**/**/b/**/c/**/*.class等
// 每次下標(biāo)又從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) {
// '**/**' 遇到連續(xù)的/**/**就跳過, 因為這沒有意義, 一個/**也可以表達多條路徑
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:
// 因為已經(jīng)確定了有 /**/a/b/**這樣的模式字符串存在, 中間2長度
// 如果存在/q/a/b/c/d 有5個長度, 那么就要循環(huán)3次
// 第一次匹配 /a/b => /q/a
// 第二次 /a/b => /a/b => 這里已經(jīng)匹配到了, 所以就break了
// /a/b => /b/c
// /a/b => /c/d
// 當(dāng)然, 如果存在更復(fù)雜的如: /**/a/b/**/a/b/**/a/b/**, 外層的while循環(huán)就會做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/.....就會在此處返回
// 當(dāng)然還有更多的
return true;
}
Spring的AntPathMatcher工具類用法
AntPathMatcher
是org.springframework.util工具包下的方法。
/**
* 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);
}
public boolean hasUrl(String url) {
if (url == null || "".equals(url)) {
return false;
}
AntPathMatcher antPathMatcher = new AntPathMatcher();
// 可根據(jù)需求做動態(tài)匹配
String pattern = "/app/*.html"
if (antPathMatcher.match(pattern, url)) {
// 根據(jù)入?yún)rl和pattern匹配上返回ture,否則false.
return true;
}
return false;
}
下面是模糊匹配規(guī)則
也就是在響應(yīng)的路徑上添加* 或則 ** 對路徑進行替代即可。
| 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 文件 |
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Reactor3 Map與FlatMap的區(qū)別示例詳解
這篇文章主要為大家介紹了Reactor3 Map與FlatMap的區(qū)別示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-08-08
SpringBoot接收請求參數(shù)的四種方式總結(jié)
這篇文章主要給大家介紹了關(guān)于SpringBoot接收請求參數(shù)的四種方式,文中通過代碼以及圖文介紹的非常詳細,對大家學(xué)習(xí)或者使用SpringBoot具有一定的參考借鑒價值,需要的朋友可以參考下2023-09-09
使用Spring的AbstractRoutingDataSource實現(xiàn)多數(shù)據(jù)源切換示例
這篇文章主要介紹了使用Spring的AbstractRoutingDataSource實現(xiàn)多數(shù)據(jù)源切換示例,具有一定的參考價值,感興趣的小伙伴們可以參考一下。2017-02-02
SpringBoot中Bean生命周期自定義初始化和銷毀方法詳解
這篇文章給大家詳細介紹了SpringBoot中Bean生命周期自定義初始化和銷毀方法,文中通過代碼示例講解的非常詳細,對大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2024-01-01
基于springboot服務(wù)間Feign調(diào)用超時的解決方案
這篇文章主要介紹了基于springboot服務(wù)間Feign調(diào)用超時的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-07-07

