← 返回题目列表

Filter 和 Spring MVC Interceptor 有什么区别?执行顺序是什么?

高频 中等 第 5 / 23 题 更新于 2026/07/25
FilterInterceptorSpring MVC请求链

简化版

Filter(过滤器)Servlet 规范的组件,工作在容器层,包在整个请求最外层,能拦截所有进入容器的请求(包括静态资源),但拿不到 Spring MVC 的 Controller 信息。Interceptor(拦截器)Spring MVC 的组件,工作在 DispatcherServlet 之后、Controller 前后,只能拦截进入 Spring MVC 的请求,但能拿到 HandlerMethod(Controller 方法、注解)。执行顺序:Filter 先包住整个请求 → 进入 DispatcherServlet → Interceptor 的 preHandle → Controller → postHandle → 视图渲染 → afterCompletion → 回到 Filter 后置

详细版

Filter vs Interceptor 对比:

维度FilterInterceptor
规范Servlet 规范(javax/jakarta)Spring MVC
所在层容器层(最外层)DispatcherServlet 内部
拦截范围所有请求(含静态资源、非 MVC)只有进入 MVC 并匹配到 Handler 的请求
能否拿到 Controller 信息能(HandlerMethod、注解)
依赖 Spring 容器否(原生)是(是 Spring Bean)
典型用途编码、CORS、压缩、安全过滤、链路追踪登录态校验、权限、接口日志、耗时统计

执行顺序(一个请求的完整流转):

请求 → Filter1.前 → Filter2.前
     → DispatcherServlet
        → Interceptor.preHandle
           → Controller 方法
        → Interceptor.postHandle(Controller 返回后、视图渲染前)
        → 视图渲染
        → Interceptor.afterCompletion(请求完成后,可清理资源)
     → Filter2.后 → Filter1.后 → 响应

Interceptor 三个方法:

  • preHandle:Controller ,返回 false 则中断请求。
  • postHandle:Controller 后、视图渲染前(可改 ModelAndView)。
  • afterCompletion:请求完成后(无论成败),适合清理 ThreadLocal。

完整版教学

一、所属层级不同(根本区别)

Filter 和 Interceptor 最本质的区别是它们工作在请求链的不同层

  • Filter 属于 Servlet 规范,由 Tomcat 等容器直接识别和调用。它包在整个请求的最外层——请求还没进 Spring 呢,就先过 Filter。所以 Filter 能拦截所有进入容器的请求,包括静态资源、非 DispatcherServlet 的请求。但正因为它太靠外,拿不到 Spring MVC 的东西(不知道请求会到哪个 Controller)。

  • Interceptor 属于 Spring MVC,是 Spring 容器里的一个 Bean。它工作在 DispatcherServlet 内部——请求必须先进入 DispatcherServlet、并匹配到一个 Handler(Controller 方法) 后,Interceptor 才参与。所以它拦截范围窄(只有 MVC 请求),但能拿到 HandlerMethod(知道要调哪个 Controller、方法上有什么注解)。

层级决定了:Filter 覆盖广但信息少,Interceptor 覆盖窄但信息多。

维度FilterInterceptor
归属Servlet 规范,容器负责调用Spring MVC 组件,由 Spring 管理
进入时机请求刚进 Web 容器时进入 DispatcherServlet 并匹配 Handler 后
可见信息主要是 ServletRequest/Response可拿到 HandlerMethod、Controller 注解
覆盖范围静态资源、MVC、其他 Servlet 都可能覆盖只覆盖 Spring MVC Handler
典型目标编码、CORS、安全链、traceId注解权限、接口耗时、业务日志

记忆钩子:Filter 像小区大门,所有人先过它;Interceptor 像某栋楼的门禁,知道你要去哪个房间,所以能按 Controller 信息做判断。

二、执行顺序详解

一个请求经过 Filter 和 Interceptor 的完整顺序(要记牢):

  1. 请求进容器,依次经过各 Filter 的前置逻辑(Filter 是嵌套的,像洋葱,Filter1 包着 Filter2)。
  2. 进入 DispatcherServlet
  3. Interceptor 的 preHandle(Controller 执行前)——多个 Interceptor 按顺序执行 preHandle。
  4. 调用 Controller 方法
  5. Interceptor 的 postHandle(Controller 返回后、视图渲染前)——多个 Interceptor 逆序执行。
  6. 视图渲染
  7. Interceptor 的 afterCompletion(请求完成后)——逆序执行,适合清理资源。
  8. 回到 Filter 的后置逻辑(逆序,Filter2 后置 → Filter1 后置)。
  9. 返回响应。

关键:Filter 在最外层「包住」整个过程(前置在最前、后置在最后),Interceptor 在 Filter 内部、Controller 前后。preHandle 正序、postHandle/afterCompletion 逆序(栈式)。

FilterA.before
  FilterB.before
    DispatcherServlet
      Interceptor1.preHandle
      Interceptor2.preHandle
        Controller
      Interceptor2.postHandle
      Interceptor1.postHandle
      view render
      Interceptor2.afterCompletion
      Interceptor1.afterCompletion
  FilterB.after
FilterA.after

如果有 2 个 Filter 和 2 个 Interceptor,你会看到 4 层“进去”和 4 层“出来”。这种栈式顺序解释了为什么清理逻辑要放在 finallyafterCompletion:越早设置的上下文,越要保证最后能被释放。

三、Filter 适合做什么

Filter 靠前、覆盖广、不依赖 Controller 信息,适合协议/容器层面的、需要覆盖所有请求的横切逻辑:

  • 统一字符编码CharacterEncodingFilter)。
  • CORS 跨域处理
  • 请求/响应包装(如包装 request 使 body 可重复读)。
  • 安全过滤Spring Security 就是通过一条 Filter 链接入 Web 请求的(它工作在 Filter 层,所以能在请求进 MVC 之前就完成认证授权)。
  • 压缩(gzip)、链路追踪(traceId 注入)、限流

这些逻辑的共同点:不需要知道请求会到哪个 Controller,且希望覆盖尽可能多的请求

四、Interceptor 适合做什么

Interceptor 能拿到 HandlerMethod(Controller 方法及其注解),适合MVC 语义更强、需要 Controller 信息的逻辑:

  • 基于注解的权限校验:读取 Controller 方法上的自定义注解(如 @RequireLogin@RequirePermission)来决定是否放行。
  • 接口耗时统计 / 业务日志:在 preHandle 记开始时间、afterCompletion 算耗时。
  • 登录态校验、租户校验
  • 统一的模型处理(postHandle 里改 ModelAndView)。

这些逻辑往往需要知道「这是哪个接口、方法上有什么注解」,所以放 Interceptor 更合适。

五、为什么有时 Interceptor「拦不到」

一个高频坑:明明配了 Interceptor,某些请求却没被拦截。原因是 Interceptor 只在「请求进入 DispatcherServlet 并匹配到 Handler」时才生效,以下情况会绕过它:

  • 静态资源(如果没走 DispatcherServlet 处理)。
  • 请求被 Filter 提前拦截返回(还没到 MVC)。
  • 不经过 DispatcherServlet 的请求(其他 Servlet)。
  • 错误转发到非 MVC 的错误页

所以:如果要求「覆盖所有入口」,必须用 Filter;如果只关心 MVC Controller,用 Interceptor 更合适。这也是为什么 Spring Security 的认证放在 Filter 层——它必须拦住一切请求,不能有漏网的。

六、资源清理都要覆盖异常路径

无论 Filter 还是 Interceptor,设置了 ThreadLocal 等上下文,就必须保证异常路径也能清理

  • Interceptor 的 afterCompletion 在请求完成后一定会执行(即使 Controller 抛异常),是清理 ThreadLocal 的好地方。
  • Filtertry...finally,在 finally 里清理。

否则,因为 Tomcat 的 Worker 线程是复用的,这次请求残留在 ThreadLocal 里的数据会被下一个复用该线程的请求读到,造成数据串号(还可能引发类加载器内存泄漏,见 Tomcat 类加载专题)。

七、选型原则

选型原则:越靠近协议和容器、需要覆盖所有请求的能力放 Filter;越靠近 Controller 语义、需要 Controller 信息的能力放 Interceptor。

  • 需要很早介入(进 MVC 前)、覆盖一切请求 → Filter(如安全、编码、CORS)。
  • 需要Controller 方法/注解信息、只关心业务接口 → Interceptor(如注解权限、接口日志)。

不要为了「都能拦截」随意选一层——关键看需要多早介入、需要哪些上下文信息、要覆盖多大范围

八、常见误区与追问

  • 误区:Filter 和 Interceptor 只是名字不同。 它们属于不同规范和层级,Filter 在 Servlet 容器层,Interceptor 在 Spring MVC 层,能拿到的上下文完全不同。
  • 误区:Interceptor 能拦截所有 URL。 它只能拦截进入 DispatcherServlet 且匹配到 Handler 的请求,静态资源、其他 Servlet 或被 Filter 提前返回的请求可能拦不到。
  • 追问:为什么 Spring Security 放在 Filter 链? 安全认证授权要尽早覆盖入口,不能等到 MVC Handler 匹配后才处理,所以它通过 Servlet Filter 链接入。
  • 追问:多个 Interceptor 的后置顺序为什么是逆序? 因为它们像调用栈一样嵌套,先进入的最后退出,便于外层做统一收尾和清理。
  • 误区:ThreadLocal 清理只在成功请求里做就够了。 请求异常时 Worker 线程仍会复用,不清理会把上次请求上下文带到下一次请求。
  • 追问:基于 Controller 注解做权限应放哪一层? 更适合 Interceptor,因为它能拿到 HandlerMethod 和方法注解;Filter 通常不知道目标 Controller 方法。

九、加强记忆

Filter(Servlet 规范、容器层、最外层)能拦所有请求(含静态资源、非 MVC)但拿不到 Controller 信息Interceptor(Spring MVC、DispatcherServlet 内部)只拦进入 MVC 并匹配到 Handler 的请求能拿 HandlerMethod(方法+注解)。执行顺序:Filter 前 → DispatcherServlet → Interceptor.preHandle(正序)→ Controller → postHandle(逆序,渲染前)→ 视图渲染 → afterCompletion(逆序,请求完成后)→ Filter 后Filter 适合编码/CORS/压缩/安全(Spring Security 走 Filter 链)/链路追踪Interceptor 适合注解权限/接口日志/耗时统计。Interceptor 拦不到静态资源和被 Filter 提前拦截的请求——要覆盖一切用 Filter。清理 ThreadLocal 要覆盖异常路径(afterCompletion / finally),否则 Worker 线程复用导致串号。