Filter执行顺序
web.xml情况下的执行顺序
在使用web.xml
进行配置的情况下,filter的执行顺序很容易确定,以下是Servlet3官方定义中的描述:
- 与请求相匹配的,
filter-mapping
元素中包含url-pattern
的Filter,将以它们在web.xml
中出现的顺序被添加到过滤器链中 - 与请求相匹配的,
filter-mapping
元素中包含一个servlet-name
的Filter,将被添加到过滤器链中<url-pattern>
匹配的所有Filter后面,也是以它们在web.xml
中出现的顺序 - 过滤器链中的最后一个项目总是原始请求资源
url-pattern的测试
下面我们有两个Filter,authFilter
用于校验用户是否登录,并将它带到合适的位置,characterEncodingFilter
将使用合适的编码解析前端请求、编码后端给前端返回的响应。我们希望任何时候,characterEncodingFilter
总能排在最前面,因为我们希望若authFitler
中也用到前端的请求参数、或返回一些内容给前端时,它的编码也是正确的。
下面的定义中,两个filter-mapping
元素都满足官方定义中的第一条,而characterEncodingFitler
定义在前,所以characterEncodingFilter
先执行。
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>top.yudoge.webreview2.filters.CharacterEncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>authFilter</filter-name>
<filter-class>top.yudoge.webreview2.filters.AuthFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>authFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
前端进行请求:
官方定义中说,顺序的标准是由filter-mapping
元素出现的先后定义,我们试试把filter
元素以相反的顺序放置,看看会不会受到影响,按官方定义来说应该是不会。
<filter>
<filter-name>authFilter</filter-name>
<filter-class>top.yudoge.webreview2.filters.AuthFilter</filter-class>
</filter>
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>top.yudoge.webreview2.filters.CharacterEncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>authFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
前端访问后的结果,确实不会:
servlet-name的测试
我从没这样用过Filter,按官方的说法,这是给指定的Servlet添加Filter,所有最终由这个Servlet处理的请求都会经过这个Filter。
现在,我们在上面的基础上,向名为loginServlet
的Servlet添加一个Filter,注意,这个Filter的filter-mapping
元素在web.xml
的位置在之前的两个之上:
<filter>
<filter-name>testFilterOne</filter-name>
<filter-class>top.yudoge.webreview2.filters.TestFilterOne</filter-class>
</filter>
<filter-mapping>
<filter-name>testFilterOne</filter-name>
<servlet-name>loginServlet</servlet-name>
</filter-mapping>
现在我们访问loginServlet
,按照官方的说法,应该是由servlet-name
所匹配的Filter的优先级不如由url-pattern
所匹配的Filter,所以,它应该在最后:
在testFilterOne
上面添加testFilterTwo
<filter>
<filter-name>testFilterTwo</filter-name>
<filter-class>top.yudoge.webreview2.filters.TestFilterTwo</filter-class>
</filter>
<filter-mapping>
<filter-name>testFilterTwo</filter-name>
<servlet-name>loginServlet</servlet-name>
</filter-mapping>
<filter>
<filter-name>testFilterOne</filter-name>
<filter-class>top.yudoge.webreview2.filters.TestFilterOne</filter-class>
</filter>
<filter-mapping>
<filter-name>testFilterOne</filter-name>
<servlet-name>loginServlet</servlet-name>
</filter-mapping>
位置在上的testFilterTwo
在另一个之前被触发:
下面调整filter-mapping
的顺序,让testFilterTwo
在下面,结果是它们被调用的顺序也发生了变化:
<filter-mapping>
中具有多个匹配方式的情况
下面是官方定义中的一个例子
对于这种定义,容器必须将它展开,所以它等价于:
web.xml情况下的总结
由<url-pattern>
匹配的Filter的调用顺序由<filter-mapping>
元素在web.xml
中的位置决定,靠上的先被调用。
由<servlet-name>
匹配的Filter总是在所有由<url-pattern>
匹配的Filter调用之后被调用,它们之间的顺序也是由它们在web.xml
中的位置决定的。
若<filter-mapping>
中有多个匹配方式,这时相当于展开的多个<filter-mapping>
的顺序情况。
注解情况下的执行顺序
Servlet3.0定义中并没有对注解情况下的Filter执行顺序,也就是说它的顺序由实现决定,网上有人查看Tomcat的源码后发现是按照简单类名的字典序排序的。
现在,删除web.xml
中所有filter相关的定义,在CharacterEncodingFilter
和AuthFilter
上添加@WebFilter
注解:
结果,是AuthFilter
在前被调用:
修改它们的类名为Filter01_CharacterEncodingFilter
和Filter02_AuthFilter
,如果是按照类名的字典序,那么这次应该是CharacterEncodingFilter
先被调用。
结果确实是这样,CharacterEncodingFilter
被先调用了:
但是本着尽量不要写出与特定实现相关的代码的原则,这种代码还是不要写。而且修改后的类名让人很摸不着头脑。
使用注解搭配web.xml
虽然确实,在@WebFilter
中添加一个类似order
的属性在技术上没什么难的,但是关键的问题是,这样的意义很有限。
考虑使用第三方框架的情况,第三方框架为了实现功能往往要向FilterChain中注入一批Filter,框架往往希望它的Filter总是最先被调用。但是框架编写的时候不了解你的代码中Filter如何定义,你使用框架时也不了解框架的代码中Filter如何定义,这些问题导致无论是你还是框架开发者都难以使用注解的order
属性来定义具体的执行顺序。可能正是因为这个考虑,Servlet3.0标准中没有在注解里提供一个order
属性,牺牲了在不使用其它框架时的易用性。
不过,我们可以通过使用注解+web.xml共同描述一个Filter,使用注解让容器可以自动扫描到一个Filter,使用web.xml的<filter-mapping>
元素描述顺序。
两个Filter类上的注解:
@WebFilter(filterName = "characterEncodingFilter")
@WebFilter(filterName = "authFilter")
web.xml中,将characterEncodingFilter
的<filter-mapping>
注解注册在前:
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>authFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
结果: