杰瑞科技汇

Java Filter 执行顺序如何确定?

这是一个非常核心且重要的面试点,也是实际开发中容易出错的地方。

Java Filter 执行顺序如何确定?-图1
(图片来源网络,侵删)

核心结论(一句话总结)

Filter 的执行顺序主要由两个因素决定:

  1. web.xml 中声明的顺序:对于使用传统部署描述符(web.xml)配置的 Filter,它们的执行顺序与它们在 <filter-mapping> 中声明的顺序一致。
  2. 类名(ClassName)的字母顺序:对于使用注解(@WebFilter)配置的 Filter,它们的执行顺序是根据 Filter 类名的字典序(lexicographical order)排列的。

详细解释

传统 web.xml 配置方式

这是最经典、最明确的配置方式,在这种方式下,顺序由开发者在 web.xml 文件中手动指定。

规则: Filter 的执行顺序与它们在 <filter-mapping> 标签中出现的顺序完全一致,先声明的先执行,后声明的后执行。

示例: 假设我们有三个 Filter:FilterA, FilterB, FilterC

Java Filter 执行顺序如何确定?-图2
(图片来源网络,侵删)
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <!-- 1. 定义 Filter -->
    <filter>
        <filter-name>FilterA</filter-name>
        <filter-class>com.example.FilterA</filter-class>
    </filter>
    <filter>
        <filter-name>FilterB</filter-name>
        <filter-class>com.example.FilterB</filter-class>
    </filter>
    <filter>
        <filter-name>FilterC</filter-name>
        <filter-class>com.example.FilterC</filter-class>
    </filter>
    <!-- 2. 映射 Filter,这里的顺序决定了执行顺序 -->
    <filter-mapping>
        <filter-name>FilterA</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <filter-mapping>
        <filter-name>FilterB</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <filter-mapping>
        <filter-name>FilterC</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

执行流程(对某个请求):

  1. 请求进入FilterA.doFilter() 被调用。
    • FilterAdoFilter 方法中,通常会调用 chain.doFilter(request, response);
  2. 传递到下一个FilterB.doFilter() 被调用。
    • FilterBdoFilter 方法中,调用 chain.doFilter(request, response);
  3. 传递到下一个FilterC.doFilter() 被调用。
    • FilterCdoFilter 方法中,调用 chain.doFilter(request, response);
  4. 到达目标 Servlet:此时请求真正到达了目标 Servlet(或 JSP)进行处理。

响应返回时的顺序(非常重要!): 响应会沿着与请求时完全相反的路径返回。

  1. Servlet 处理完毕,返回响应。
  2. FilterCchain.doFilter(...) 代码行之后继续执行。
  3. 然后从 FilterBchain.doFilter(...) 代码行之后继续执行。
  4. 最后从 FilterAchain.doFilter(...) 代码行之后继续执行。
  5. 响应返回给客户端。

口诀: “先进后出,后进先出”,就像一个栈(Stack)。


注解 @WebFilter 配置方式

从 Servlet 3.0 开始,推荐使用注解来配置 Filter,在这种方式下,顺序规则有所不同。

规则: 如果多个 @WebFilter 注解的 Filter 映射到了相同的 URL 模式,那么它们的执行顺序是根据 Filter 类名的字典序来决定的。

示例: 我们有三个 Filter 类:

  • com.example.security.AuthenticationFilter
  • com.example.logging.LoggingFilter
  • com.example.performance.MonitoringFilter

它们的类名分别是:AuthenticationFilter, LoggingFilter, MonitoringFilter

执行顺序(按字典序):

  1. AuthenticationFilter (A...)
  2. LoggingFilter (L...)
  3. MonitoringFilter (M...)

代码示例:

// AuthenticationFilter.java
@WebFilter("/*")
public class AuthenticationFilter implements Filter {
    // ...
}
// LoggingFilter.java
@WebFilter("/*")
public class LoggingFilter implements Filter {
    // ...
}
// MonitoringFilter.java
@WebFilter("/*")
public class MonitoringFilter implements Filter {
    // ...
}

在这个例子中,所有 Filter 都映射到了 ,所以它们的执行顺序将是 AuthenticationFilter -> LoggingFilter -> MonitoringFilter

重要提示:

  • 类名全小写比较:比较的是类名的完整字符串,通常是全小写。MyFilterAmyfiltera 是一样的。
  • 不推荐依赖注解顺序:类名排序可能看起来很随意,不够直观,如果顺序至关重要,强烈建议使用 web.xml 方式或 @WebFilterdispatcherTypesurlPatterns 组合来精确控制。

混合配置方式(web.xml + @WebFilter

在一个项目中,可能同时存在 web.xml@WebFilter 定义的 Filter,顺序规则如下:

  1. web.xml 中的 Filter 总是优先执行,无论它们的类名是什么,也不管它们在 web.xml 中的声明顺序如何,它们都会先于所有 @WebFilter 定义的 Filter 执行。
  2. @WebFilter 之间的顺序:遵循注解的规则,即按类名字典序执行。

示例:

  • web.xml 中定义了 XmlFilterAXmlFilterB
  • 使用注解定义了 AnnotFilterXAnnotFilterY

最终执行顺序:

  1. XmlFilterA (来自 web.xml)
  2. XmlFilterB (来自 web.xml)
  3. AnnotFilterX (来自注解,类名排序靠前)
  4. AnnotFilterY (来自注解,类名排序靠后)

如何精确控制顺序(最佳实践)

当顺序至关重要时(认证必须在日志之前),有几种可靠的方法:

  1. 首选:使用 web.xml 这是最清晰、最不容易出错的方式,将所有对顺序有要求的 Filter 都在 web.xml 中声明,并严格按照业务逻辑顺序排列 <filter-mapping>

  2. 次选:使用 @WebFilterdispatcherTypes 有时可以通过 dispatcherTypes 来间接控制,一个只处理 REQUEST 的 Filter 和一个只处理 ERROR 的 Filter,它们的执行时机不同,不会产生顺序冲突。

  3. 高级:实现 javax.servlet.Filter 接口的 jakarta.servlet.FilterChain (不推荐,了解即可) 在非常特殊的情况下,你可以创建一个自定义的 FilterChain 实例,并在 doFilter 方法中手动调用下一个 Filter,这会完全覆盖容器的默认链式调用机制,非常复杂且不推荐。

总结表格

配置方式 顺序规则 优点 缺点
web.xml <filter-mapping> 的声明顺序 清晰、明确、可控,业界标准 需要维护 XML 文件,略显繁琐
@WebFilter Filter 类名的字典序 配置简单,代码内聚 顺序不可预测,依赖类名,不直观
混合配置 web.xml 中的 Filter 优先。
@WebFilter 之间按类名排序。
兼容旧项目 规则复杂,容易混淆,不推荐混合使用

最佳建议: 如果你的项目使用 Servlet 3.0+ 并且对 Filter 顺序有严格要求,强烈建议回归使用 web.xml 来配置 Filter,这能避免很多潜在的部署问题,也让代码逻辑对其他开发者更加清晰,如果顺序不重要,使用 @WebFilter 会更简洁。

分享:
扫描分享到社交APP
上一篇
下一篇