Loading

Filter执行顺序

web.xml情况下的执行顺序

在使用web.xml进行配置的情况下,filter的执行顺序很容易确定,以下是Servlet3官方定义中的描述:

  1. 与请求相匹配的,filter-mapping元素中包含url-pattern的Filter,将以它们在web.xml中出现的顺序被添加到过滤器链中
  2. 与请求相匹配的,filter-mapping元素中包含一个servlet-name的Filter,将被添加到过滤器链中<url-pattern>匹配的所有Filter后面,也是以它们在web.xml中出现的顺序
  3. 过滤器链中的最后一个项目总是原始请求资源

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>

前端进行请求:

img

官方定义中说,顺序的标准是由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>

前端访问后的结果,确实不会:

img

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,所以,它应该在最后:

img

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在另一个之前被触发:

img

下面调整filter-mapping的顺序,让testFilterTwo在下面,结果是它们被调用的顺序也发生了变化:

img

<filter-mapping>中具有多个匹配方式的情况

下面是官方定义中的一个例子

img

对于这种定义,容器必须将它展开,所以它等价于:

img

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相关的定义,在CharacterEncodingFilterAuthFilter上添加@WebFilter注解:

img

结果,是AuthFilter在前被调用:

img

修改它们的类名为Filter01_CharacterEncodingFilterFilter02_AuthFilter,如果是按照类名的字典序,那么这次应该是CharacterEncodingFilter先被调用。

结果确实是这样,CharacterEncodingFilter被先调用了:

img

但是本着尽量不要写出与特定实现相关的代码的原则,这种代码还是不要写。而且修改后的类名让人很摸不着头脑。

使用注解搭配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>

结果:

img

posted @ 2022-07-21 09:54  yudoge  阅读(1142)  评论(0编辑  收藏  举报