Spring源码解析 - AntPathMatcher
最近在看SpringMVC的源码,发现request分发时,路径匹配最后是委托给AntPathMatcher实现的.索性看看吧.
文章摘要:
1. ant匹配规则
2. PathMatcher接口
3. 通过测试用例看AntPathMatcher的使用
ant匹配规则
AntPathMatcher如名使用的ant 的匹配规则,我们先看看吧.
字符wildcard 描述
? 匹配一个字符
* 匹配0个及以上字符
** 匹配0个及以上目录directories
看几个官方的例子吧:
com/t?st.jsp - 匹配: com/test.jsp , com/tast.jsp , com/txst.jsp
com/*.jsp - 匹配: com文件夹下的全部.jsp文件
com/**/test.jsp - 匹配: com文件夹和子文件夹下的全部.jsp文件,
org/springframework/**/*.jsp - 匹配: org/springframework文件夹和子文件夹下的全部.jsp文件
org/**/servlet/bla.jsp - 匹配: org/springframework/servlet/bla.jsp , org/springframework/testing/servlet/bla.jsp , org/servlet/bla.jsp
PathMatcher接口
主要是判断是否匹配pattern,并解析出path中的参数
1 package org.springframework.util; 2 3 public interface PathMatcher { 4 5 /** 6 * 判断传入的path是否可以作为pattern使用 7 */ 8 boolean isPattern(String path); 9 10 /** 11 * 使用pattern匹配path 12 */ 13 boolean match(String pattern, String path); 14 15 /** 16 * 如名,是否开始部分匹配 17 */ 18 boolean matchStart(String pattern, String path); 19 20 /** 21 * 提取path中匹配到的部分,如pattern(myroot/*.html),path(myroot/myfile.html),返回myfile.html 22 */ 23 String extractPathWithinPattern(String pattern, String path); 24 25 /** 26 * 提取path中匹配到的部分,只是这边还需跟占位符配对为map, 27 * 如pattern(/hotels/{hotel}),path(/hotels/1),解析出"hotel"->"1" 28 */ 29 Map<String, String> extractUriTemplateVariables(String pattern, String path); 30 31 /** 32 * 提供比较器 33 */ 34 Comparator<String> getPatternComparator(String path); 35 36 /** 37 * 合并pattern,pattern1然后pattern2 38 */ 39 String combine(String pattern1, String pattern2); 40 41 }
通过测试用例看AntPathMatcher的使用
一看测试用例,瞬间服了,人家开发真是规范.
人家整这么规范,还是有空直接看源码好了.这边挑几个简单的例子看看就好
1. match 跟 matchStart 的差异,这个我们在测试用例看下面的情况会比较明确
这边的代码,我截取了一小部分
1 package org.springframework.util; 2 public class AntPathMatcherTests { 3 @Test 4 public void match() { 5 // ... 6 assertFalse(pathMatcher.match("/x/x/**/bla", "/x/x/x/")); 7 // ... 8 } 9 @Test 10 public void withMatchStart() { 11 // ... 12 assertTrue(pathMatcher.matchStart("/x/x/**/bla", "/x/x/x/")); 13 // ... 14 } 15 }
2. extractPathWithinPattern,代码很清楚,不废话
1 package org.springframework.util; 2 public class AntPathMatcherTests { 3 @Test 4 public void extractPathWithinPattern() throws Exception { 5 // ... 6 assertEquals("", pathMatcher.extractPathWithinPattern("/docs/commit.html", "/docs/commit.html")); 7 assertEquals("cvs/commit", pathMatcher.extractPathWithinPattern("/docs/*", "/docs/cvs/commit")); 8 assertEquals("docs/cvs/commit", pathMatcher.extractPathWithinPattern("/d?cs/*", "/docs/cvs/commit")); 9 // ... 10 } 11 }
3. extractUriTemplateVariables
1 package org.springframework.util; 2 public class AntPathMatcherTests { 3 @Test 4 public void extractUriTemplateVariables() throws Exception { 5 Map<String, String> result = pathMatcher.extractUriTemplateVariables("/hotels/{hotel}", "/hotels/1"); 6 assertEquals(Collections.singletonMap("hotel", "1"), result); 7 // ... 8 result = pathMatcher.extractUriTemplateVariables("/{page}.*", "/42.html"); 9 assertEquals(Collections.singletonMap("page", "42"), result); 10 // ... 11 } 12 /** 13 * SPR-7787 14 */ 15 @Test 16 public void extractUriTemplateVarsRegexQualifiers() { 17 Map<String, String> result = pathMatcher.extractUriTemplateVariables( 18 "{symbolicName:[\\p{L}\\.]+}-sources-{version:[\\p{N}\\.]+}.jar", 19 "com.example-sources-1.0.0.jar"); 20 assertEquals("com.example", result.get("symbolicName")); 21 assertEquals("1.0.0", result.get("version")); 22 // ... 23 } 24 }
4. combine
1 package org.springframework.util; 2 public class AntPathMatcherTests { 3 @Test 4 public void combine() { 5 // ... 6 assertEquals("/hotels", pathMatcher.combine("/hotels", null)); 7 assertEquals("/hotels/booking", pathMatcher.combine("/hotels/*", "/booking")); 8 assertEquals("/hotels/**/booking", pathMatcher.combine("/hotels/**", "booking")); 9 assertEquals("/hotels/**/booking", pathMatcher.combine("/hotels/**", "/booking")); 10 assertEquals("/hotels/booking", pathMatcher.combine("/hotels", "/booking")); 11 assertEquals("/hotels/{hotel}", pathMatcher.combine("/hotels/*", "{hotel}")); 12 assertEquals("/hotels/**/{hotel}", pathMatcher.combine("/hotels/**", "{hotel}")); 13 assertEquals("/hotels/*/booking/{booking}", pathMatcher.combine("/hotels/*/booking", "{booking}")); 14 } 15 }