使用Spring AntPathMatcher的doMatch方法
AntPathMatcher的doMatch方法
AntPathMatcher.doMatch(...), 是解決模式匹配的源碼
有4個(gè)步驟
1. 分解模式字符串, 分解路徑字符串
2. 第一個(gè)while 循環(huán), 用來(lái)判斷絕對(duì)匹配 /xxx/abc ==> /xxx/abc
3. 第二個(gè)while循環(huán)兩個(gè)字符串?dāng)?shù)組都從最后的下標(biāo)開(kāi)始匹配, 直到遇到pattDir為'**'時(shí)結(jié)束
4. 第三個(gè)while循環(huán), 主要解決有多個(gè)'**'字符串./**/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. 第一個(gè)while 循環(huán), 用來(lái)判斷絕對(duì)匹配的 /xxx/abc ==> /xxx/abc
// 兩個(gè)字符串都從下標(biāo)0開(kāi)始, 直到模式字符串遇到**結(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) {
// 判斷最后一個(gè)字符是否為'/'
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;
}
// 不會(huì)執(zhí)行到這里
for (int i = pattIdxStart; i <= pattIdxEnd; i++) {
if (!pattDirs[i].equals("**")) {
return false;
}
}
*/
// 這里返回true 一般是相等的字符串匹配(長(zhǎng)度相同)
// /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. 兩個(gè)字符串?dāng)?shù)組都從最后的下標(biāo)開(kāi)始匹配, 直到遇到pattDir為'**'時(shí)結(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
// 即只有一個(gè)**, 而且**沒(méi)發(fā)揮到什么作用
// 測(cè)試
// AntPathMatcher ant = new AntPathMatcher("/");
//String pattern = "/abc/**/*.class";
//String path = "/abc/ddd.class";
//System.out.println(ant.match(pattern, path));
return true;
}
// 4. 第3個(gè)while循環(huán), 主要解決有多個(gè)'**'字符串. /**/djdjdjd/**, /a/**/**/b/**/c/**/*.class等
// 每次下標(biāo)又從pattIdxStart+1開(kāi)始
while (pattIdxStart != pattIdxEnd && pathIdxStart <= pathIdxEnd) {
int patIdxTmp = -1; // 這個(gè)用來(lái)指向**的位置
for (int i = pattIdxStart + 1; i <= pattIdxEnd; i++) {
if (pattDirs[i].equals("**")) {
patIdxTmp = i;
break;
}
}
if (patIdxTmp == pattIdxStart + 1) {
// '**/**' 遇到連續(xù)的/**/**就跳過(guò), 因?yàn)檫@沒(méi)有意義, 一個(gè)/**也可以表達(dá)多條路徑
pattIdxStart++;
continue;
}
// patLength: 兩個(gè)'**'之間的字符串的長(zhǎng)度 /**/a/b/** = 2
// strLength: 路徑還剩多少個(gè)沒(méi)匹配 /a/b/c/d 如果/a/b都匹配了, 就只剩下/b/c = 2
int patLength = (patIdxTmp - pattIdxStart - 1);
int strLength = (pathIdxEnd - pathIdxStart + 1);
int foundIdx = -1;
strLoop:
// 因?yàn)橐呀?jīng)確定了有 /**/a/b/**這樣的模式字符串存在, 中間2長(zhǎng)度
// 如果存在/q/a/b/c/d 有5個(gè)長(zhǎng)度, 那么就要循環(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)就會(huì)做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;
}
}
//如果上面的都沒(méi)有返回值 /** => /sdjdd/djkd/.....就會(huì)在此處返回
// 當(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ù)需求做動(dòng)態(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)的路徑上添加* 或則 ** 對(duì)路徑進(jìn)行替代即可。
| URL路徑 | 說(shuō)明 |
| /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 文件 |
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
java接口返回參數(shù)按照請(qǐng)求參數(shù)進(jìn)行排序方式
這篇文章主要介紹了java接口返回參數(shù)按照請(qǐng)求參數(shù)進(jìn)行排序方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09
java集合Collection實(shí)現(xiàn)類解析ArrayList?LinkedList及Vector
這篇文章主要為大家介紹了java集合Collection實(shí)現(xiàn)類解析ArrayList?LinkedList及Vector,有需要的朋友可以借鑒參考下,希望能夠有所幫助2022-03-03
關(guān)于java自定義線程池的原理與實(shí)現(xiàn)
本文介紹了如何自定義線程池和阻塞隊(duì)列,包括阻塞隊(duì)列的實(shí)現(xiàn)方法,線程池的構(gòu)建以及拒絕策略的應(yīng)用,詳細(xì)闡述了線程池中任務(wù)的提交和執(zhí)行流程,以及如何處理任務(wù)超出隊(duì)列容量的情況2022-04-04
SpringBoot WebSocket實(shí)時(shí)監(jiān)控異常的詳細(xì)流程
最近做了一個(gè)需求,消防的設(shè)備巡檢,如果巡檢發(fā)現(xiàn)異常,通過(guò)手機(jī)端提交,后臺(tái)的實(shí)時(shí)監(jiān)控頁(yè)面實(shí)時(shí)獲取到該設(shè)備的信息及位置,然后安排員工去處理。這篇文章主要介紹了SpringBoot WebSocket實(shí)時(shí)監(jiān)控異常的全過(guò)程,感興趣的朋友一起看看吧2021-10-10
解決Java調(diào)用BAT批處理不彈出cmd窗口的方法分析
本篇文章是對(duì)Java調(diào)用BAT批處理不彈出cmd窗口的方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05
Spring-data-redis操作redis cluster的示例代碼
這篇文章主要介紹了Spring-data-redis操作redis cluster的示例代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-10-10
Spring學(xué)習(xí)筆記之RestTemplate使用小結(jié)
這篇文章主要給大家介紹了關(guān)于Spring學(xué)習(xí)筆記之RestTemplate使用的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-08-08

