Spring 过滤器(Filter)与拦截器(Interceptor)深度对比:原理与实战
作为一名摸爬滚打八年的 Java 开发,我敢说:90% 的 Spring 开发者都踩过 Filter 和 Interceptor 的坑。上周线上排查一个权限校验失效的故障,最后发现是新人把 “登录态校验” 写在了 Filter 里,却因为 Filter 无法注入 Spring Bean 导致校验逻辑形同虚设 —— 这种因为混淆两者差异引发的问题,在我八年职业生涯里见过太多次。
今天就从 “原理拆解 + 实战落地 + 踩坑避坑” 三个维度,用最接地气的方式把 Filter 和 Interceptor 讲透。不管你是刚入行的新手,还是想夯实基础的资深开发者,读完这篇都能彻底分清什么时候该用哪个,再也不踩配置和选型的坑。
一、先搞懂:两者到底是什么?(通俗原理拆解)
很多人觉得 Filter 和 Interceptor 都是 “拦截请求” 的,功能差不多 —— 但其实两者的底层逻辑和设计目标完全不同,用两个比喻就能秒懂:
Filter:Web 容器的 “小区保安”
Filter 是Servlet 规范的一部分,属于 Web 容器层面的组件,不依赖 Spring 框架就能独立工作。它就像小区门口的保安,不管你是去住户家(Controller 请求)、去便利店(静态资源)、还是去快递柜(未映射 URL),都必须经过它的检查。
- 核心原理:基于责任链模式,Web 容器(Tomcat)会维护一条 Filter 链,请求必须依次通过所有 Filter 才能到达目标资源,响应则反向回流。
- 生命周期:由 Servlet 容器管理,web 应用启动时初始化(init 方法执行一次),销毁时调用 destroy 方法释放资源,全程与 Spring 容器无关。
- 核心方法:只有
doFilter一个核心方法,同时处理请求前置逻辑和响应后置逻辑,通过FilterChain控制是否放行。
Interceptor:Spring 容器的 “公司前台”
Interceptor 是Spring MVC 框架的专属组件,依赖 Spring 容器才能工作。它就像公司前台,只负责拦截 “找员工办事”(Controller 请求)的人,对去茶水间(静态资源)、会议室(非 MVC 请求)的人完全不干预。
-
核心原理:基于AOP 思想,拦截的是 Spring MVC 的处理器(Handler)执行流程,只能在 DispatcherServlet 分发的请求链路中生效。
-
生命周期:由 Spring 容器管理,支持依赖注入(@Autowired),可以直接使用 Service、Mapper 等 Spring Bean。
-
核心方法:三个回调方法覆盖请求全流程,粒度更细:
preHandle:Controller 方法执行前触发(返回 true 放行,false 中断);postHandle:Controller 执行后、视图渲染前触发(可修改 ModelAndView);afterCompletion:视图渲染完成后触发(无论成功失败都会执行,适合资源清理)。
二、核心差异:9 个维度对比(一张表看透)
光靠比喻不够,实战中选型需要精准把握差异。下面这张表是我八年开发总结的核心对比维度,覆盖配置、功能、场景等关键信息,建议收藏:
| 对比维度 | 过滤器(Filter) | 拦截器(Interceptor) |
|---|---|---|
| 规范归属 | Servlet 规范(Jakarta EE 标准) | Spring MVC 框架机制 |
| 依赖关系 | 不依赖 Spring,由 Servlet 容器管理 | 强依赖 Spring,需 Spring 容器初始化 |
| 拦截范围 | 所有请求(含静态资源、未映射 URL) | 仅拦截 Spring MVC 的 Controller 请求 |
| 可访问对象 | 仅能操作 ServletRequest/Response 原始对象 | 可获取 HandlerMethod、ModelAndView 等 Spring 对象 |
| 依赖注入 | 直接 @Autowired 失效(需特殊配置) | 支持正常 @Autowired 注入 Spring Bean |
| 执行时机 | DispatcherServlet 之前 / 之后执行 | DispatcherServlet 内部,Controller 前后执行 |
| 配置粒度 | 支持 URL Pattern 通配符,全局生效为主 | 支持路径包含 / 排除,可精准匹配接口 |
| 异常处理 | 无法参与 Spring 异常处理流程 | 可配合 @ControllerAdvice 统一处理异常 |
| 典型场景 | 编码设置、跨域处理、XSS 过滤、全链路追踪 | 权限校验、业务日志、请求参数预处理、响应包装 |
💡 关键结论:Filter 管 “全局基础设施”,Interceptor 管 “业务逻辑增强”—— 这是两者最核心的定位差异,也是选型的首要依据。
三、实战落地:Spring Boot 3.x 环境下的具体实现
理论讲完,直接上可落地的代码。注意:Spring Boot 3.x 已全面迁移到 Jakarta EE 9+,Filter 的包名从javax.servlet改为jakarta.servlet,这是很多人踩坑的点,下面代码直接用最新规范。
场景 1:用 Filter 实现 XSS 过滤(全局安全防护)
XSS 过滤是所有请求都需要的安全防护,适合用 Filter 实现,拦截所有 URL:
1. 实现 Filter 逻辑
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Component;
import java.io.IOException;
// 全局XSS过滤器:过滤请求参数中的恶意脚本
@Component
public class XssFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 包装请求对象,重写getParameter方法实现XSS过滤
XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request);
// 放行到下一个Filter或目标资源
chain.doFilter(xssRequest, response);
}
// 自定义请求包装类:实现参数过滤逻辑
static class XssHttpServletRequestWrapper extends jakarta.servlet.http.HttpServletRequestWrapper {
public XssHttpServletRequestWrapper(HttpServletRequest request) {
super(request);
}
@Override
public String getParameter(String name) {
String value = super.getParameter(name);
return value == null ? null : cleanXss(value);
}
// XSS过滤核心逻辑(简化版,实际可使用成熟工具类)
private String cleanXss(String value) {
return value.replaceAll("<", "<")
.replaceAll(">", ">")
.replaceAll("'", "'")
.replaceAll(""", """);
}
}
}
2. 注册 Filter(精准控制拦截范围)
用FilterRegistrationBean手动注册,比@WebFilter更灵活,支持配置拦截路径和执行顺序:
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<XssFilter> xssFilterRegistration(XssFilter xssFilter) {
FilterRegistrationBean<XssFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(xssFilter);
registration.addUrlPatterns("/api/*"); // 只拦截API请求
registration.setOrder(Ordered.HIGHEST_PRECEDENCE); // 最高优先级,先执行XSS过滤
registration.setName("xssFilter");
return registration;
}
}
场景 2:用 Interceptor 实现权限校验(业务相关拦截)
权限校验需要结合用户状态、角色信息,依赖 Spring 的 Service 层,适合用 Interceptor 实现:
1. 实现 Interceptor 逻辑
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
// 权限拦截器:校验用户是否登录、是否有操作权限
@Component
@RequiredArgsConstructor
public class AuthInterceptor implements HandlerInterceptor {
private final UserService userService; // 直接注入Spring Bean,无需特殊配置
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// 1. 非Controller请求(如静态资源)直接放行
if (!(handler instanceof HandlerMethod handlerMethod)) {
return true;
}
// 2. 有@PermitAll注解的接口直接放行(如登录、注册)
if (handlerMethod.hasMethodAnnotation(PermitAll.class)) {
return true;
}
// 3. 从请求头获取Token,校验登录状态
String token = request.getHeader("X-Token");
if (token == null || !userService.validateToken(token)) {
response.setStatus(401);
response.getWriter().write("未登录或Token失效");
return false;
}
// 4. 校验接口权限(如需要ADMIN角色)
RequiredRole requiredRole = handlerMethod.getMethodAnnotation(RequiredRole.class);
if (requiredRole != null && !userService.hasRole(token, requiredRole.value())) {
response.setStatus(403);
response.getWriter().write("权限不足");
return false;
}
// 5. 校验通过,放行到Controller
return true;
}
// 自定义注解:用于标记无需登录的接口
public @interface PermitAll {}
// 自定义注解:用于标记接口所需角色
public @interface RequiredRole {
String value();
}
}
2. 注册 Interceptor
通过WebMvcConfigurer注册,支持精准配置拦截路径和排除路径:
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
private final AuthInterceptor authInterceptor;
// 构造函数注入Interceptor(@RequiredArgsConstructor也可)
public WebMvcConfig(AuthInterceptor authInterceptor) {
this.authInterceptor = authInterceptor;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authInterceptor)
.addPathPatterns("/api/**") // 拦截所有API接口
.excludePathPatterns("/api/login", "/api/register") // 排除登录、注册接口
.order(1); // 执行顺序(数值越小越先执行)
}
}
四、八年踩坑实录:3 个最容易掉的坑及解决方案
理论和代码都很完美,但实际开发中总有各种 “意外”。下面这 3 个坑是我和同事们踩过最多的,每个都附带根治方案:
坑 1:Filter 中 @Autowired 注入 Bean 失败(报 NullPointerException)
原因:
Filter 由 Servlet 容器初始化,而 Servlet 容器启动早于 Spring 容器,导致 Spring 无法为 Filter 注入 Bean。
解决方案(按推荐度排序):
- 用
FilterRegistrationBean注册:像上面实战那样,通过 Spring 容器管理 Filter 实例,注入的 Bean 自然有效; - 构造函数注入:在 FilterRegistrationBean 中直接传入依赖的 Bean(
new XssFilter(userService)); - 工具类获取 Bean:通过
ApplicationContextHolder工具类手动获取 Bean(适合老项目改造)。
坑 2:Interceptor 的 postHandle 方法不执行
原因:
- 要么
preHandle方法返回了false,导致后续流程中断; - 要么 Controller 方法抛出异常,或直接写入响应流(如下载文件),导致视图渲染流程跳过;
- 异步请求(Callable/DeferredResult)中,postHandle 不会执行,需实现
AsyncHandlerInterceptor。
解决方案:
- 先检查 preHandle 返回值,确保为 true;
- 异常统一用
@ControllerAdvice处理,避免 Controller 直接抛异常; - 异步请求改用
AsyncHandlerInterceptor,重写afterConcurrentHandlingStarted方法。
坑 3:多个 Filter/Interceptor 执行顺序混乱
原因:
- Filter 的执行顺序:
FilterRegistrationBean的setOrder数值越小越先执行,@WebFilter按类名字母排序; - Interceptor 的执行顺序:按
addInterceptors的注册顺序,preHandle 正序执行,postHandle 和 afterCompletion 逆序执行。
解决方案:
- Filter 统一用
FilterRegistrationBean注册,通过setOrder明确顺序(负数用于系统级 Filter,正数用于业务级); - Interceptor 按 “先安全后业务” 的顺序注册,比如先注册 AuthInterceptor,再注册 LogInterceptor。
五、选型指南:到底什么时候用哪个?
最后用一张 “决策流程图” 帮你快速选型,记住核心原则:基础设施用 Filter,业务逻辑用 Interceptor:
- 若需要拦截静态资源、处理编码 / 跨域 / XSS 等与业务无关的全局逻辑 → 用 Filter;
- 若需要依赖 Spring Bean、做权限校验 / 业务日志 / 参数预处理等业务相关逻辑 → 用 Interceptor;
- 若需要修改请求体 / 响应体(如解密、压缩) → 用 Filter(支持包装 Request/Response);
- 若需要细粒度控制(如只拦截特定注解的接口) → 用 Interceptor(可获取 HandlerMethod)。
简单总结成一句口诀: “全局拦截用 Filter,业务增强用 Interceptor” 。
六、总结
八年开发下来,我深刻体会到:框架的核心组件没有 “好坏之分”,只有 “适配之分”。Filter 和 Interceptor 的本质区别,在于它们的设计定位 —— 一个是 Web 容器的 “底层守卫”,一个是 Spring 框架的 “业务助手”。
理解了它们的底层原理和适用场景,就能在实际开发中精准选型,避免踩坑。如果你的项目中还在为 “该用 Filter 还是 Interceptor” 纠结,或者遇到了其他配置问题,欢迎在评论区留言,我会用八年实战经验帮你分析解答~