前言
AntPathMatcher是什么?主要用來解決什么問題掏婶?
背景:在做uri匹配規(guī)則發(fā)現(xiàn)這個類恐疲,根據(jù)spring源碼對該類進行分析,它主要用來做類URLs字符串匹配扯旷;
效果
可以做URLs匹配拯爽,規(guī)則如下
?匹配一個字符
*匹配0個或多個字符
**匹配0個或多個目錄
用例如下
/trip/api/*x ? ?匹配 /trip/api/x钧忽,/trip/api/ax毯炮,/trip/api/abx 逼肯;但不匹配?/trip/abc/x;
/trip/a/a?x ? ?匹配 /trip/a/abx桃煎;但不匹配?/trip/a/ax篮幢,/trip/a/abcx
/**/api/alie ? ?匹配 /trip/api/alie,/trip/dax/api/alie为迈;但不匹配?/trip/a/api
/**/*.htmlm ??匹配所有以.htmlm結尾的路徑
核心
AntPathMatcher API接口
由上圖可知三椿,AntPathMatcher提供了豐富的API,主要以doMatch為主葫辐,下邊來講doMatch的實現(xiàn)上(其中pattern為制定的url模式搜锰,path為具體的url,下邊以英文為主講解):
1? ? 首先判斷pattern和path的首字符是否同時為設置的分隔符耿战,結果不一致則直接返回false纽乱,不進行下邊的操作;
2 ? ?分別對pattern和path進行分詞昆箕,形成各自的字符串數(shù)組鸦列,其中分詞的主要代碼如下(這段代碼很清晰):
注:str代表要進行分詞的字符串,delimiters是進行分詞的分隔符鹏倘,trimTokens表示是否對每一個分詞進行首尾去空字符串薯嗤,ignoreEmptyTokens代表分割之后是否保留空字符串;
我們發(fā)現(xiàn)纤泵,每次計算這個也是要花費一定的時間消耗骆姐,那每次真的是要重新計算么 ?看下邊的代碼來找答案(下邊的代碼是在上個方法tokenizeToStringArray調用之前進行):
我們看到捏题,這里存了一個pattern的cachetokenizedPatternCache玻褪,key為pattern,value為分次之后的字符串數(shù)組公荧,每次先到cache獲取带射,沒有的話則計算,然后放入到cache里邊循狰,這樣在做頻繁的url mapping的時候窟社,由于規(guī)則是有限的,可以很大程度減少計算绪钥;
同理灿里,path也是通過同樣的計算,不過程腹,path則不會緩存匣吊,每次都需要調用tokenizeToStringArray進行分詞(為什么呢?[1])
接著來說3:
3 ? ?對分詞之后的pattern數(shù)組和path數(shù)組從begin進行遍歷,一旦pattern的第一個字符串是**的話色鸳,則跳出來侣灶,此時沒有直接返回true,為什么呢[2]缕碎?
下邊接著看doMatch的中間部分代碼(也就是說當break或者運行完畢while循環(huán)的時候褥影,在退出之前會接著執(zhí)行下邊的代碼)
1? ? 如果path分詞數(shù)組正常執(zhí)行完畢,則pathIdxStart是會比pathIdxEnd大1的咏雌,這個時候凡怎,如果pattern的字符串數(shù)組也正常耗盡,則來判斷pattern和path的最后一個字符是否同步赊抖,按結果返回统倒;
2 ? ?如果上邊的循環(huán)只執(zhí)行了一次,則這時候pattIdxStart則和pattIdxEnd相等氛雪,同時pattern的最后一個字符是*且path最后是一個分隔符房匆,則直接返回true;
3 ? ?如果pattern的最后一個字符串是**則path不需要判斷直接返回true报亩;
4 ? ?這一步代表浴鸿,pattern已經(jīng)耗盡但是path還沒耗盡,這時候肯定不匹配弦追,直接返回false
接下來接著看岳链,緊接著上邊第二幅黑色背景圖,如果第一次因為**彈出來劲件,看下邊如何處理:
這個時候掸哑,開始從后往前遍歷,如果再次彈出來不是因為遇到了**零远,是正常遍歷完成苗分,這個時候,pathIdxStart是大于pathIdxEnd牵辣,這個時候字符串已經(jīng)耗盡摔癣,如果pattern還沒有耗盡,并且最后并不是**服猪,則直接返回false供填;
如果中間再次出現(xiàn)**拐云,并且path并沒有耗盡罢猪,則進行下邊的步驟:
這一部分代碼主要用來循環(huán)處理中間再次**的情況,直到完全處理完成叉瘩,這里邊用到了Java的標簽語法:strLoop膳帕,符合條件則跳轉到strLoop(類似goto);
總結
這一部分的處理理解起來不是非常難懂,但是這個關于字符串匹配的過程是及其細致的,每一個邊界問題都想得比較完美危彩,這一點是相當值得學習的攒磨。
后記
其中,每一個path的分詞是如何匹配到pattern的分詞是怎么做的呢汤徽?答案就在matchStrings這個方法里邊了:
首先用path來匹配pattern的時候娩缰,要獲取一個matcher,代碼如下:
這里new AntPathStringMatcher(AntPathMatcher的一個內部類)的時候也是需要一些計算谒府,matcher構建的精華全部在這里了:
這部分計算比較頻繁拼坎,也會耗費一定量的時間,所以這里用到了一個叫做stringMatcherCache的cache完疫,上文中提到的兩個cache的數(shù)量都不能超過65536泰鸡,有其中任意一個cache超過這個限制,則會清空整個cache壳鹤。