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

核心结论(一句话总结)
Filter 的执行顺序主要由两个因素决定:
- 在
web.xml中声明的顺序:对于使用传统部署描述符(web.xml)配置的 Filter,它们的执行顺序与它们在<filter-mapping>中声明的顺序一致。 - 类名(ClassName)的字母顺序:对于使用注解(
@WebFilter)配置的 Filter,它们的执行顺序是根据 Filter 类名的字典序(lexicographical order)排列的。
详细解释
传统 web.xml 配置方式
这是最经典、最明确的配置方式,在这种方式下,顺序由开发者在 web.xml 文件中手动指定。
规则:
Filter 的执行顺序与它们在 <filter-mapping> 标签中出现的顺序完全一致,先声明的先执行,后声明的后执行。
示例:
假设我们有三个 Filter:FilterA, FilterB, FilterC。

<?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>
执行流程(对某个请求):
- 请求进入:
FilterA.doFilter()被调用。- 在
FilterA的doFilter方法中,通常会调用chain.doFilter(request, response);。
- 在
- 传递到下一个:
FilterB.doFilter()被调用。- 在
FilterB的doFilter方法中,调用chain.doFilter(request, response);。
- 在
- 传递到下一个:
FilterC.doFilter()被调用。- 在
FilterC的doFilter方法中,调用chain.doFilter(request, response);。
- 在
- 到达目标 Servlet:此时请求真正到达了目标 Servlet(或 JSP)进行处理。
响应返回时的顺序(非常重要!): 响应会沿着与请求时完全相反的路径返回。
- Servlet 处理完毕,返回响应。
- 从
FilterC的chain.doFilter(...)代码行之后继续执行。 - 然后从
FilterB的chain.doFilter(...)代码行之后继续执行。 - 最后从
FilterA的chain.doFilter(...)代码行之后继续执行。 - 响应返回给客户端。
口诀: “先进后出,后进先出”,就像一个栈(Stack)。
注解 @WebFilter 配置方式
从 Servlet 3.0 开始,推荐使用注解来配置 Filter,在这种方式下,顺序规则有所不同。
规则:
如果多个 @WebFilter 注解的 Filter 映射到了相同的 URL 模式,那么它们的执行顺序是根据 Filter 类名的字典序来决定的。
示例: 我们有三个 Filter 类:
com.example.security.AuthenticationFiltercom.example.logging.LoggingFiltercom.example.performance.MonitoringFilter
它们的类名分别是:AuthenticationFilter, LoggingFilter, MonitoringFilter。
执行顺序(按字典序):
AuthenticationFilter(A...)LoggingFilter(L...)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。
重要提示:
- 类名全小写比较:比较的是类名的完整字符串,通常是全小写。
MyFilterA和myfiltera是一样的。 - 不推荐依赖注解顺序:类名排序可能看起来很随意,不够直观,如果顺序至关重要,强烈建议使用
web.xml方式或@WebFilter的dispatcherTypes和urlPatterns组合来精确控制。
混合配置方式(web.xml + @WebFilter)
在一个项目中,可能同时存在 web.xml 和 @WebFilter 定义的 Filter,顺序规则如下:
web.xml中的 Filter 总是优先执行,无论它们的类名是什么,也不管它们在web.xml中的声明顺序如何,它们都会先于所有@WebFilter定义的 Filter 执行。@WebFilter之间的顺序:遵循注解的规则,即按类名字典序执行。
示例:
web.xml中定义了XmlFilterA和XmlFilterB。- 使用注解定义了
AnnotFilterX和AnnotFilterY。
最终执行顺序:
XmlFilterA(来自 web.xml)XmlFilterB(来自 web.xml)AnnotFilterX(来自注解,类名排序靠前)AnnotFilterY(来自注解,类名排序靠后)
如何精确控制顺序(最佳实践)
当顺序至关重要时(认证必须在日志之前),有几种可靠的方法:
-
首选:使用
web.xml这是最清晰、最不容易出错的方式,将所有对顺序有要求的 Filter 都在web.xml中声明,并严格按照业务逻辑顺序排列<filter-mapping>。 -
次选:使用
@WebFilter的dispatcherTypes有时可以通过dispatcherTypes来间接控制,一个只处理REQUEST的 Filter 和一个只处理ERROR的 Filter,它们的执行时机不同,不会产生顺序冲突。 -
高级:实现
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 会更简洁。
