使用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工具類(lèi)用法
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 MyBatis入門(mén)學(xué)習(xí)過(guò)程記錄
MyBatis是一個(gè)支持普通SQL查詢,存儲(chǔ)過(guò)程和高級(jí)映射的優(yōu)秀持久層框架。這篇文章主要介紹了mybatis框架入門(mén)學(xué)習(xí)教程,需要的朋友可以參考下,希望能幫助到你2021-06-06一個(gè)簡(jiǎn)單JDK版動(dòng)態(tài)代理
這篇文章主要為大家詳細(xì)介紹了一個(gè)簡(jiǎn)單JDK版動(dòng)態(tài)代理,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-09-09Reactor3 Map與FlatMap的區(qū)別示例詳解
這篇文章主要為大家介紹了Reactor3 Map與FlatMap的區(qū)別示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08Java使用Cipher類(lèi)實(shí)現(xiàn)加密的過(guò)程詳解
這篇文章主要介紹了Java使用Cipher類(lèi)實(shí)現(xiàn)加密的過(guò)程詳解,Cipher類(lèi)提供了加密和解密的功能,創(chuàng)建密匙主要使用SecretKeySpec、KeyGenerator和KeyPairGenerator三個(gè)類(lèi)來(lái)創(chuàng)建密匙。感興趣可以了解一下2020-07-07Java實(shí)戰(zhàn)之客戶信息管理系統(tǒng)
這篇文章主要介紹了Java實(shí)戰(zhàn)之客戶信息管理系統(tǒng),文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java的小伙伴們有非常好的幫助,需要的朋友可以參考下2021-04-04SpringBoot接收請(qǐng)求參數(shù)的四種方式總結(jié)
這篇文章主要給大家介紹了關(guān)于SpringBoot接收請(qǐng)求參數(shù)的四種方式,文中通過(guò)代碼以及圖文介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用SpringBoot具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-09-09使用Spring的AbstractRoutingDataSource實(shí)現(xiàn)多數(shù)據(jù)源切換示例
這篇文章主要介紹了使用Spring的AbstractRoutingDataSource實(shí)現(xiàn)多數(shù)據(jù)源切換示例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-02-02SpringBoot中Bean生命周期自定義初始化和銷(xiāo)毀方法詳解
這篇文章給大家詳細(xì)介紹了SpringBoot中Bean生命周期自定義初始化和銷(xiāo)毀方法,文中通過(guò)代碼示例講解的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2024-01-01基于springboot服務(wù)間Feign調(diào)用超時(shí)的解決方案
這篇文章主要介紹了基于springboot服務(wù)間Feign調(diào)用超時(shí)的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07