过滤器和拦截器的区别是什么?
结论先行:Filter 是 Servlet 规范定义的组件,由容器(Tomcat)在请求进入 DispatcherServlet 之前调用,能改请求/响应对象;HandlerInterceptor 是 Spring MVC 的组件,在 DispatcherServlet 内部、Handler 执行前后调用,能拿到 HandlerMethod(哪个 Controller 的哪个方法)。一句话:Filter 管「进容器」,Interceptor 管「进 Controller」。
1. 在请求链路中的位置
客户端
│
▼
FilterChain(容器级,可多个)
│ ① Filter.doFilter
▼
DispatcherServlet
│
├─ HandlerMapping 找到 Handler
├─ HandlerInterceptor.preHandle ← 返回 false 则中断
├─ HandlerAdapter 调用 Controller
├─ HandlerInterceptor.postHandle
├─ 视图渲染 / @ResponseBody 序列化
└─ HandlerInterceptor.afterCompletion
│
▼
Filter 的后置逻辑(chain.doFilter 之后的代码)
顺序很关键:Filter 在 DispatcherServlet 外,Interceptor 在里,所以 Filter 的 doFilter 后置代码最后执行。
2. 对比表
| 维度 | Filter | HandlerInterceptor |
|---|---|---|
| 规范来源 | Servlet(javax/jakarta.servlet) | Spring MVC |
| 作用范围 | 所有进入容器的请求(含静态资源) | 只有被 DispatcherServlet 处理的请求 |
| 触发时机 | 请求进入容器后、Servlet 之前 | Handler 查找之后、执行前后 |
| 能否拿到 Handler | 否,只有 ServletRequest | 能,preHandle 参数含 HandlerMethod |
| 典型用途 | 编码、CORS、XSS 过滤、请求体包装、全链路 TraceId | 登录校验、权限、日志、参数预处理 |
| 中断方式 | 不调 chain.doFilter | preHandle 返回 false |
3. 代码:Filter
@Component
@Order(1) // 数值越小越先执行
public class TraceIdFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
String traceId = req.getHeader("X-Trace-Id");
if (traceId == null) {
traceId = UUID.randomUUID().toString().replace("-", "");
}
MDC.put("traceId", traceId); // 日志链路
HttpServletResponse resp = (HttpServletResponse) response;
resp.setHeader("X-Trace-Id", traceId);
try {
chain.doFilter(request, response); // 放行
} finally {
MDC.clear(); // 防止线程复用导致串号
}
}
}
注册方式:@Component 即可(Spring Boot 自动注册),或用 FilterRegistrationBean 精确控制顺序与 URL 匹配。
4. 代码:Interceptor
@Component
public class AuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
if (!(handler instanceof HandlerMethod method)) {
return true; // 静态资源等直接放行
}
if (method.hasMethodAnnotation(PublicApi.class)) {
return true; // 白名单注解
}
String token = request.getHeader("Authorization");
if (token == null || !JwtUtil.verify(token)) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
return false; // 中断,不再进入 Controller
}
return true;
}
}
@Configuration
@RequiredArgsConstructor
public class WebMvcConfig implements WebMvcConfigurer {
private final AuthInterceptor authInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authInterceptor)
.addPathPatterns("/api/**")
.excludePathPatterns("/api/auth/**", "/error");
}
}
5. 怎么选
| 需求 | 选择 | 原因 |
|---|---|---|
| 统一编码、CORS、请求体缓存 | Filter | 需要在 Servlet 解析参数前处理 |
| 全链路 TraceId、访问日志 | Filter | 覆盖所有请求,包括 404 |
| 登录/权限校验 | Interceptor | 能读注解、能拿到 HandlerMethod |
| 需要环绕 Controller 的 AOP 语义 | 拦截器或 AOP | Filter 拿不到方法信息 |
6. 常见追问
追问一:Filter 里能注入 Spring Bean 吗?
能,但必须由 Spring 管理(@Component 或 FilterRegistrationBean)。若在 web.xml 里配置,容器创建的是自己的实例,@Autowired 不生效,需要用 WebApplicationContextUtils 手动取。
追问二:多个 Filter 的顺序怎么定?
@Order 或 FilterRegistrationBean.setOrder(),数值小的先执行。Spring Security 的过滤器链 order 是 -100,业务过滤器要排在它之后(数值更大),否则拿不到 SecurityContext。
追问三:preHandle 返回 false 后,afterCompletion 还会执行吗?
不会。只有 preHandle 返回 true 的拦截器才会在请求完成后调用 afterCompletion。所以资源释放要放在 afterCompletion 里,并注意它与 preHandle 的配对。
追问四:Filter 和 AOP 有什么区别? AOP 基于方法调用,能精确到方法、参数、返回值,适合事务、日志;Filter 基于 HTTP 请求,粒度是「请求」,拿不到方法签名。三者常组合:Filter 做编码与 TraceId,Interceptor 做鉴权,AOP 做业务日志。
追问五:OncePerRequestFilter 是什么?
Spring 提供的 Filter 基类,保证一次请求只执行一次(内部用 request attribute 标记)。异步请求、forward 场景下,普通 Filter 可能被多次调用,用它更安全。
小结
区别可以压缩成一句话:Filter 是容器的门卫,Interceptor 是 MVC 的前台。门卫能查所有进出的人但不知道你要找谁;前台知道你要找哪个部门(HandlerMethod),但只管进了大楼的人。选型就看你要不要「方法级信息」和「是否覆盖静态资源」。