bigywater.tech
Spring · 44

拦截器与 Servlet Filter:执行顺序与能看到什么

拦截器和 Filter 到底谁先谁后?preHandle 返回 false 之后会发生什么?404 的请求能不能被拦住?在 Filter 里读了请求体为什么 Controller 就读不到了?这篇在 Spring Boot 4.0.8 的 Spring MVC 7.0.9 上逐个打印执行顺序。

Filter ⊃ 拦截器
Filter 先进入、最后返回;拦截器链被包在里面
B 没有 afterCompletion
B 的 preHandle 返回 false 后,B.afterCompletion 不会执行,A 的会
400
Filter 读完请求体之后,@RequestBody 的结果

执行顺序

实测 · 本机执行输出
  GET /api/hi → 200 hi
      F1 进入 /api/hi [REQUEST]
      F2 进入 /api/hi [REQUEST]
      Once(OncePerRequestFilter) 进入 /api/hi [REQUEST]
      A.preHandle [REQUEST] handler=hi
      B.preHandle [REQUEST] handler=hi
            Controller /api/hi
      B.postHandle
      A.postHandle
      B.afterCompletion
      A.afterCompletion
      F2 返回
      F1 返回
  GET /open/hi → 200 open
      F1 进入 /open/hi [REQUEST]
      F2 进入 /open/hi [REQUEST]
      Once(OncePerRequestFilter) 进入 /open/hi [REQUEST]
      A.preHandle [REQUEST] handler=open
            Controller /open/hi
      A.postHandle
      A.afterCompletion
      F2 返回
      F1 返回

Filter → 拦截器 preHandle(升序)→ Controller → postHandle、afterCompletion(倒序)→ Filter 返回。

中止

实测 · 本机执行输出
  GET /api/hi → 403 
      F1 进入 /api/hi [REQUEST]
      F2 进入 /api/hi [REQUEST]
      Once(OncePerRequestFilter) 进入 /api/hi [REQUEST]
      A.preHandle [REQUEST] handler=hi
      B.preHandle [REQUEST] handler=hi
      B 返回 false,中止
      A.afterCompletion
      F2 返回
      F1 返回

preHandle 返回 false 后只对已返回 true 的拦截器调用 afterCompletion。

能看到什么

实测 · 本机执行输出
  GET /nope → 404 {"timestamp":"2026-10-19T03:34:18.256Z",
      F1 进入 /nope [REQUEST]
      F2 进入 /nope [REQUEST]
      Once(OncePerRequestFilter) 进入 /nope [REQUEST]
      A.preHandle [REQUEST] handler=ResourceHttpRequestHandler
      A.afterCompletion
      F2 返回
      F1 返回
  GET /static-missing.css → 404 {"timestamp":"2026-10-19T03:34:18.477Z",
      F1 进入 /static-missing.css [REQUEST]
      F2 进入 /static-missing.css [REQUEST]
      Once(OncePerRequestFilter) 进入 /static-missing.css [REQUEST]
      F2 返回
      F1 返回

Filter 对所有请求生效,拦截器只对匹配路径、有处理器的请求生效,并能拿到 HandlerMethod。

读 body

实测 · 本机执行输出
  POST /api/body → 200 got:payload
      F1 进入 /api/body [REQUEST]
      F2 进入 /api/body [REQUEST]
      Once(OncePerRequestFilter) 进入 /api/body [REQUEST]
      A.preHandle [REQUEST] handler=body
      B.preHandle [REQUEST] handler=body
            Controller 读到 body = payload
      B.postHandle
      A.postHandle
      B.afterCompletion
      A.afterCompletion
      F2 返回
      F1 返回

Servlet 输入流只能读一次,Filter 里读 body 要用缓存包装类。

读 body 的后果

实测 · 本机执行输出
  POST /api/body → 400 {"timestamp":"2026-10-19T03:34:20.710Z",
      F1 进入 /api/body [REQUEST]
      F2 进入 /api/body [REQUEST]
      Once(OncePerRequestFilter) 进入 /api/body [REQUEST]
      BodyReader 在 Filter 里把请求体读完了:payload
      A.preHandle [REQUEST] handler=body
      B.preHandle [REQUEST] handler=body
      B.afterCompletion
      A.afterCompletion
      F2 返回
      F1 返回

Filter 读完 body,@RequestBody 读不到,返回 400。

分步演示

拦截器与 Filter
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK 21.0.12(Homebrew) / Spring Boot 4.0.8(Spring Framework 7.0.9、Tomcat 11.0.24) / Spring Cloud 2025.1.3;用 javac 编译、java 直接运行,依赖由 Maven 从 Maven Central 解析;数字每次运行会有小幅抖动。用 Spring Boot 4.0.8 的 SpringApplication(spring-boot-starter-webmvc,内嵌 Tomcat 11);Filter 用 FilterRegistrationBean 设置 order,拦截器用 WebMvcConfigurer.addInterceptors;每一行事件由应用自己打印,不是框架日志
    • 没有验证:异步请求(DeferredResult、Callable)上拦截器与 Filter 的二次调度与 AsyncHandlerInterceptor;ERROR 派发与 FORWARD 派发下 Filter 的注册范围;ContentCachingRequestWrapper 的实际用法与内存开销;Spring Security 过滤器链与自定义 Filter 的相对位置(见 Security 章节);WebFlux 的 WebFilter
    京ICP备2026067502号-1京公网安备11010502063487号