面试必问:过滤器和拦截器谁先执行?一图看懂执行顺序与核心区别
这是一道面试频率高达 80% 的 Java Web 题目。很多开发者能随口答出“Filter 先执行”,但当被问到“为什么”以及“如何配置执行顺序”时,就开始含糊了。本文将从调用链、源码层面、配置方式等多个维度彻底讲透 Filter 与 Interceptor 的执行顺序,并辅以流程图和对比表格,让你一次掌握。
一、直接结论
过滤器(Filter)先执行,拦截器(Interceptor)后执行。
更精确地说,在一个 Spring MVC 应用中,一个请求的完整执行顺序为:
客户端 → 过滤器(Filter) → 拦截器(Interceptor) → Controller → 拦截器(后置) → 过滤器(后置) → 客户端如果项目中同时使用了 Filter 和 Interceptor,那么Filter 总是先于 Interceptor 执行,也晚于 Interceptor 结束(后置处理顺序相反)。
二、为什么 Filter 先于 Interceptor?—— 原理层面
要理解顺序,首先必须搞清楚 Filter 和 Interceptor 分别位于 Web 容器的哪一层。
2.1 Filter(过滤器) —— Servlet 容器级别
- Filter 是Java Servlet 规范中定义的组件,位于Servlet 容器(如 Tomcat、Jetty)内部。
- Filter 可以拦截所有进入 Servlet 容器的请求(包括静态资源、JSP、Servlet)。
- 一个或多个 Filter 形成FilterChain,请求在进入
Servlet之前会依次经过所有匹配的 Filter。
2.2 Interceptor(拦截器) —— Spring MVC 级别
- Interceptor 是Spring MVC 框架提供的组件,位于
DispatcherServlet调用Controller的过程当中。 - Interceptor 只能拦截由
DispatcherServlet处理的请求(通常是对应@Controller的请求路径),无法拦截静态资源(除非配置)。 - Interceptor 依赖于 Spring 容器,可以利用 Spring 的依赖注入、AOP 等特性。
结论:由于 Filter 工作在 Servlet 容器层面,而 Interceptor 工作在更上层的 Spring MVC 内部,因此请求势必先经过 Filter 才会到达 Interceptor。
三、完整调用时序图(附代码级顺序)
下面通过一个详细的时序图,展示从请求进入容器到响应返回的全流程,包含多个 Filter 和多个 Interceptor 的执行顺序。
执行顺序总结:
- Filter 的
doFilter()前置代码(按配置顺序)- Interceptor 的
preHandle()(按配置顺序)- Controller 方法
- Interceptor 的
postHandle()(按配置逆序)- 视图渲染(若适用)
- Interceptor 的
afterCompletion()(按配置逆序)- Filter 的
doFilter()后置代码(按配置逆序)
四、代码验证(Spring Boot 示例)
4.1 定义一个简单的 Filter
@ComponentpublicclassMyFilterimplementsFilter{@OverridepublicvoiddoFilter(ServletRequestrequest,ServletResponseresponse,FilterChainchain)throwsIOException,ServletException{System.out.println("Filter.doFilter() 前置");chain.doFilter(request,response);System.out.println("Filter.doFilter() 后置");}}4.2 定义一个拦截器
@ComponentpublicclassMyInterceptorimplementsHandlerInterceptor{@OverridepublicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler){System.out.println("Interceptor.preHandle()");returntrue;}@OverridepublicvoidpostHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler,ModelAndViewmv){System.out.println("Interceptor.postHandle()");}@OverridepublicvoidafterCompletion(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler,Exceptionex){System.out.println("Interceptor.afterCompletion()");}}4.3 配置拦截器(Spring Boot)
@ConfigurationpublicclassWebConfigimplementsWebMvcConfigurer{@AutowiredprivateMyInterceptormyInterceptor;@OverridepublicvoidaddInterceptors(InterceptorRegistryregistry){registry.addInterceptor(myInterceptor).addPathPatterns("/**");}}4.4 请求一个 Controller 接口,控制台输出结果:
Filter.doFilter() 前置 Interceptor.preHandle() --- 执行 Controller 方法 --- Interceptor.postHandle() Interceptor.afterCompletion() Filter.doFilter() 后置结论:Filter 的前置逻辑确实在 Interceptor 的
preHandle之前执行;而 Filter 的后置逻辑在 Interceptor 的所有后置方法完成之后才执行。
五、多 Filter / 多 Interceptor 的配置顺序如何影响执行顺序?
5.1 Filter 顺序控制
Spring Boot 中,Filter 的执行顺序由以下方式决定:
- 使用
@Order(1)注解(数字越小越先执行)。 - 实现
Ordered接口。 - 通过
FilterRegistrationBean设置setOrder()。
示例:
@Bean@Order(1)publicFilterfilterA(){returnnewMyFilterA();}@Bean@Order(2)publicFilterfilterB(){returnnewMyFilterB();}执行顺序:FilterA 前置 → FilterB 前置 → … → FilterB 后置 → FilterA 后置。
5.2 Interceptor 顺序控制
使用addInterceptor的顺序决定:
registry.addInterceptor(interceptorA).addPathPatterns("/**");registry.addInterceptor(interceptorB).addPathPatterns("/**");preHandle执行顺序:A 先,B 后。postHandle与afterCompletion执行顺序:B 先,A 后(逆序)。
5.3 混合顺序示意图
六、Filter 和 Interceptor 的核心区别对比表
| 维度 | Filter | Interceptor |
|---|---|---|
| 规范层级 | Java Servlet 规范 | Spring MVC / 框架级 |
| 容器依赖 | Servlet 容器(Tomcat/Jetty) | Spring 容器(IoC) |
| 拦截范围 | 可以拦截所有请求(包括静态资源、JSP) | 只能拦截由 DispatcherServlet 处理的请求 |
| 方法数量 | 1 个核心方法:doFilter | 3 个核心方法:preHandle,postHandle,afterCompletion |
| 资源访问 | 无法直接使用 Spring 的 Bean(需特殊处理) | 可直接注入任何 Spring 组件 |
| 适用场景 | 编码/字符集过滤、鉴权、日志、跨域处理 | 权限校验、日志、参数注入、性能监控 |
| 执行时机 | 请求进入 Servlet 容器后最早执行 | 在 DispatcherServlet 调用 Controller 前后 |
| 是否支持 AOP | 不支持(但可通过包装 Request/Response 变相实现) | 配合 Spring AOP 更灵活 |
七、常见面试追问与解答
Q1:能否让 Interceptor 比 Filter 先执行?
不能,因为 Filter 位于 Servlet 容器层面,Interceptor 位于 Spring MVC 层面。请求必须先经过 Servlet 容器才能到达 DispatcherServlet,这是物理上的层级限制。除非你把自己的逻辑写在 Filter 之前(如 Servlet 容器的 Valve),但那不属于 Filter/Interceptor 范畴。
Q2:Filter 和 Interceptor 都做权限校验,应该用哪个?
推荐使用 Filter。因为:
- Filter 更早执行,可以提前拒绝非法请求,减少不必要的 Spring MVC 处理。
- Filter 不依赖 Spring,可以用于非 Spring 环境(但现代项目这点不是主要因素)。
- 但 Interceptor 可以更精细地获取 HandlerMethod 信息(如方法上的注解),如果用 Spring Security,它默认基于 Filter。
实际项目常见做法:全局 Token 校验用 Filter,方法级权限用 Spring Security 或 Interceptor + 注解。
Q3:如果在 Filter 中直接返回响应(不调用 chain.doFilter),Interceptor 还会执行吗?
不会。chain.doFilter()负责将请求传递给下一个 Filter 或 Servlet。如果 Filter 直接response.getWriter().write(...)并返回,整个调用链中断,DispatcherServlet 不会被调用,因此 Interceptor 也不会执行。
Q4:如何控制多个 Filter 和 Interceptor 的终止?
- Filter 中不调用
chain.doFilter()即可终止。 - Interceptor 中
preHandle返回false即可终止后续 Interceptor 和 Controller 的执行,但已经执行过的 Filter 后置逻辑仍会执行(因为 Filter 的doFilter方法已经过了chain.doFilter调用点)。
八、总结
| 关键点 | 结论 |
|---|---|
| 执行顺序 | Filter 先,Interceptor 后 |
| 原因 | Filter 工作在 Servlet 容器层,Interceptor 工作在 Spring MVC 层 |
| Filter 多实例顺序 | 由@Order或FilterRegistrationBean决定(数字小优先) |
| Interceptor 多实例顺序 | addInterceptor的添加顺序决定 preHandle 顺序,逆序决定 postHandle/afterCompletion |
| Filter 终止 | 不调用chain.doFilter() |
| Interceptor 终止 | preHandle返回false |
一句话总结面试回答:Filter 先执行,因为它是 Servlet 容器的组件,位于 Spring MVC 的拦截器之前。如果需要详细展开,可以补充说明 Filter 和 Interceptor 各自所处的层级和调用链。
九、拓展思考
- 如果项目同时使用了 Filter、Interceptor 和 AOP(如
@Around),三者的执行顺序如何?欢迎动手实验。 - 如何在 Filter 中获取 Spring 管理的 Bean?可以使用
WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext).getBean(...)。
📚参考阅读:Spring 官方文档——Interceptors