见:http://blog.csdn.net/haoyifen/article/details/52679576
关于Spring的路径, 问的最多的问题就是如何拦截所有以.do结尾的请求.
大家都是在猜, 网上给出的答案五花八门, 比如: *do, /*do, /**do, /** 等..然而这些都是错的.
正确答案是/**/*.do, 如果要拦截/api下的所有.do结尾的请求, 那么应该使用/api/**/*.do, 以此类推.
我觉得这是一个很恶心的东西. 关于字符串匹配, 存在三个常用的规则: 正则, AntMatcher和通配符. 恶心的是这三者都使用了?和*, 而且意义各不同. 真是一件让程序员苦恼的事.
言归正传, Spring中无论是handler路径映射, 还是interceptor路径匹配, 还是sping.xml中加载配置文件时使用的”classpath*”, 都使用的是AntMathcer规则( 见PathMatcher文档), 实现类是org.springframework.util.AntPathMatcher. 这里是这个类的官方文档AntPathMatcher文档
里面有这样的说明
匹配符 | 规则 | |
---|---|---|
? | 匹配一个字符 | |
* | 匹配0或更多字符 | |
** | 匹配path路径中的0个或更多目录 | |
{spring:[a-z]+} | 匹配正则[a-z]+并作为一个路径变量, 赋值给变量spring |
注意在AntPathMatcher中, 有个路径分隔符的概念, 路径分隔符是不能被 ? 或单个 * 匹配的, 路径分隔符默认是 / , 在匹配类全限定名时, 设置成 . 其他规则不变.
以下是简单的介绍: (注意**可以匹配0个或更多的目录)
规则 | 简介 | 匹配 | 不匹配 |
---|---|---|---|
/goods/t?st.jsp | /goods路径下只相差一个字符的路径 | /good/test.jsp /goods/text.jsp /goods/tast.jsp等 | /goods/taast.jsp /google/tast.jsp等 |
/**/test.jsp | 匹配任何以test.jsp结尾的路径 | /test.jsp/goods/test.jsp /my/goods/test.jsp /my/favorite/goods/test.jsp等 | /goods/abc.jsp等 |
/**/*.jsp | 任何以jsp结尾的路径 | /test.jsp /goods/test.jsp /my/goods/test.jsp等 | /hello.html /my/hello.html等 |
/goods/** | 匹配goods下的所有路径 | /goods /goods/a.html /goods/a.jsp /goods/test/a.jsp等 | 非/goods/下的路径 |
/或/goods | 只匹配/或/goods这一个路径,千万不要误认为是所有路径 | 只有/和/goods | 其他任何路径 |
/** | 匹配所有路径 | /test/goods/test /test.html /test.jsp /goods/test.jsp /my/goods/test.jsp等 | 无 |
完整用法可见Sping的托管在github的测试代码AntPathMatcherTests.java
以下是我从中摘取的一部分. 可以不用看这个.
// 完全匹配
assertTrue(pathMatcher.match("test", "test"));
assertTrue(pathMatcher.match("/test", "/test"));
//不匹配
assertFalse(pathMatcher.match("/test.jpg", "test.jpg"));
assertFalse(pathMatcher.match("test", "/test"));
// 使用?进行匹配, ?表示匹配一个字符
assertTrue(pathMatcher.match("t?st", "test"));
assertTrue(pathMatcher.match("??st", "test"));
//不匹配
assertFalse(pathMatcher.match("tes?", "tes"));
assertFalse(pathMatcher.match("tes?", "testt"));
//使用*进行匹配, *匹配0 更多字符
assertTrue(pathMatcher.match("*", "test"));
assertTrue(pathMatcher.match("test*", "test"));
assertTrue(pathMatcher.match("test*", "testTest"));
assertTrue(pathMatcher.match("test/*", "test/Test"));
assertTrue(pathMatcher.match("test/*", "test/t"));
assertTrue(pathMatcher.match("*.*", "test."));
assertTrue(pathMatcher.match("*.*", "test.test.test"));
//以下不匹配
assertFalse(pathMatcher.match("test*", "tst"));
assertFalse(pathMatcher.match("test*", "tsttest"));
assertFalse(pathMatcher.match("test*", "test/"));
assertFalse(pathMatcher.match("test/*", "test"));
// 使用?和/一起匹配
assertTrue(pathMatcher.match("/?", "/a"));
assertTrue(pathMatcher.match("/?/a", "/a/a"));
assertTrue(pathMatcher.match("/a/?", "/a/b"));
assertTrue(pathMatcher.match("/??/a", "/aa/a"));
assertTrue(pathMatcher.match("/a/??", "/a/bb"));
assertTrue(pathMatcher.match("/?", "/a"));
// 使用**匹配, 匹配0个或更多目录
assertTrue(pathMatcher.match("/**", "/testing/testing"));
assertTrue(pathMatcher.match("/*/**", "/testing/testing"));
assertTrue(pathMatcher.match("/**/*", "/testing/testing"));
assertTrue(pathMatcher.match("/bla/**/bla", "/bla/testing/testing/bla"));
assertTrue(pathMatcher.match("/bla/**/bla", "/bla/testing/testing/bla/bla"));
assertTrue(pathMatcher.match("/*bla*/**/bla/**", "/XXXblaXXXX/testing/testing/bla/testing/testing/"));
assertTrue(pathMatcher.match("/*bla*/**/bla/*", "/XXXblaXXXX/testing/testing/bla/testing"));
assertTrue(pathMatcher.match("*bla*/**/bla/**", "XXXblaXXXX/testing/testing/bla/testing/testing"));
assertFalse(pathMatcher.match("*bla*/**/bla/*", "XXXblaXXXX/testing/testing/bla/testing/testing"));
assertTrue(pathMatcher.match("/foo/bar/**", "/foo/bar")) ;
assertTrue(pathMatcher.match("/{bla}.*", "/testing.html"));