在web.xml中<servlet-mapping>和<filter-mapping>都有<url-pattern>配置项,他们的作用都是匹配一次请求是否会执行这个Servlet或者Filter,那么这个URL是怎么匹配的,又是何时匹配的呢?
先看看Servlet是何时匹配的。一个请求最终被分配到一个Servlet中是通过org.apache.tomcat.util.http.Mapper类完成的,这个类会根据请求的URL来匹配在每个Servlet中配置的<url-pattern>,所以他在一个请求被创建时就已经匹配了。Filter的url-pattern匹配是在创建ApplicationFilterChain对象时进行的,他会把所有定义的Filter的url-pattern与当前的URL匹配,如果匹配成功就将这个Filter保存到ApplicationFilterChain的filters数组中,然后在FilterChain中依次调用。
在web加载时,会首先检查<url-pattern>配置是否符合规则,这个检查是在StandardContext的validateURLPattern方法中检查的,如果检查不成功,Context容器启动会失败,并且会报java.lang.IllegalArgumentException:Invalid<url-pattern>/a/*.htm in Servlet mapping错误。
<url-pattern>的解析规则,对Servlet和Filter是一样的,匹配的规则有如下三种。
- 精确匹配:如/foo.htm只会匹配foo.htm这个URL。
- 路径匹配:如/foo/*会匹配以foo为前缀的URL。
- 后缀匹配:如*.htm会匹配所有以.htm为后缀的URL。
Servlet的匹配规则在org.apache.tomcat.util.http.mapper.Mapper.internalMapWrapper中定义,对Servlet的匹配来说如果同时定义了多个<url-pattern>,那么到底匹配那个Servlet呢?这个匹配顺序:首先精确匹配,如定义了两个Servlet,Servlet1为/foo.htm,Servlet2是/*,请求URL为http://localhost/foo.htm,那么只有Servlet1匹配成功;如果精确匹配不成功,那么会使用第二个原则“最长路径匹配”,,如Servlet1为/foo/*,Servlet2为/*,这时请求的URL为http://localhost/foo/foo.htm,那么Servlet1匹配成功;最后根据后缀进行匹配,但是一次请求只会成功匹配到一个Servlet。
Filter的匹配规则在ApplicationFilterFactory.matchFiltersURL方法中定义。Filter的匹配原则和Servlet有些不同,只要匹配成功,这些Filter都会在请求链上被调用。<url-pattern>的其他写法(如/foo/、/*.htm和*/foo)都是不对的。