Spring 里的 Filter 和 Interceptor 到底有什么区别?
这是 Spring 项目中非常高频的一个面试/实战问题。
Filter 和 Interceptor 都能实现“在请求到达 Controller 前/后做一些统一处理”,但它们底层机制、执行时机、能做的事情、控制粒度完全不同。
下面用表格 + 核心维度 + 代码示例,把两者的区别讲透。
一、核心对比表(面试最常用版本)
| 维度 | Filter (javax.servlet.Filter / jakarta.servlet.Filter) | Interceptor (HandlerInterceptor) | 谁更常用?(2025-2026) |
|---|---|---|---|
| 所属规范 | Servlet 规范(Web 容器层面) | Spring MVC 框架层面 | — |
| 执行时机 | 在 DispatcherServlet 之前(请求进入 Servlet 容器后最早) | 在 DispatcherServlet 之后,HandlerMapping 之后 | — |
| 作用范围 | 所有到达 Servlet 的请求(包括 .jsp、静态资源、Servlet 等) | 只作用于 Spring MVC 管理的请求(Controller) | Filter 范围更大 |
| 能否获取 Spring 容器中的 Bean | 不能(容器启动时初始化) | 可以(通过 Spring 管理) | Interceptor 更强 |
| 能否修改 request/response | 可以(包装 Request/Response) | 只能在 pre/post 阶段有限修改(基本靠 HttpServletRequestWrapper) | Filter 更灵活 |
| 能否阻止请求继续 | 可以(不调用 chain.doFilter() 就阻断) | 可以(preHandle 返回 false 就阻断) | 差不多 |
| 异常处理能力 | 无法处理 Controller 抛出的异常 | 可以(afterCompletion 能捕获) | Interceptor 更强 |
| 执行顺序 | 先执行 Filter → 再执行 Interceptor | — | Filter 先 |
| 典型使用场景 | 编码转换、字符集处理、CORS、全局 XSS 过滤、请求日志、静态资源放行 | 登录校验、权限校验、日志记录、性能监控、国际化参数注入 | 看需求 |
| 是否能注入 Spring Bean | 传统 Filter 不行(需配合 DelegatingFilterProxy) | 天生可以(@Component 或配置类注册) | Interceptor 更方便 |
| 配置方式 | web.xml 或 @WebFilter + @ServletComponentScan | 实现 HandlerInterceptor + 加入 WebMvcConfigurer | — |
二、执行顺序图(非常重要,面试常画)
客户端请求
↓
Servlet Filter(多个,按配置顺序)
↓
DispatcherServlet(Spring MVC 前置控制器)
↓
HandlerMapping(找 Controller)
↓
**HandlerInterceptor.preHandle()** ← 这里可以阻断
↓
Controller 方法执行
↓
**HandlerInterceptor.postHandle()** ← 响应生成前
↓
视图渲染(ViewResolver)
↓
**HandlerInterceptor.afterCompletion()** ← 整个请求结束(可捕获异常)
↓
Servlet Filter(响应回来的路,逆序)
↓
返回客户端
一句话总结执行顺序:
Filter 先于 Interceptor 执行,Interceptor 后于 Filter 执行。
三、代码对比(最直观)
1. Filter 示例(全局字符编码 + 日志)
@WebFilter(urlPatterns = "/*")
@Order(1)
public class EncodingFilter implements Filter {
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
System.out.println("Filter before: " + request.getRequestURI());
chain.doFilter(req, resp);
System.out.println("Filter after");
}
}
2. Interceptor 示例(登录校验)
@Component
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
// 可以直接 @Autowired UserService
String token = request.getHeader("Authorization");
if (StringUtils.isBlank(token)) {
response.setStatus(401);
return false;
}
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
// 可以记录耗时、异常日志
if (ex != null) {
log.error("Controller 异常", ex);
}
}
}
注册方式:
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/login", "/static/**");
}
}
四、实际项目中怎么选?
| 需求场景 | 推荐使用 | 原因简述 |
|---|---|---|
| 全局编码转换、CORS、请求/响应包装 | Filter | 范围最大、最早执行 |
| 静态资源放行、swagger、druid 监控页面放行 | Filter | 这些请求不进 Spring MVC |
| 登录、权限、角色校验 | Interceptor | 能获取 Spring 容器资源,逻辑更丰富 |
| 记录 Controller 方法耗时、异常统一处理 | Interceptor | afterCompletion 能捕获异常 |
| 需要同时做请求和响应处理的复杂逻辑 | Filter | 可以完整包装 request/response |
| 微服务网关层统一鉴权、限流 | Filter / GatewayFilter | 更早拦截 |
五、面试一句话总结(背下来)
“Filter 是 Servlet 层面的,范围更大、更早执行,适合做通用 Web 容器级处理;Interceptor 是 Spring MVC 层面的,粒度更细,能方便使用 Spring 容器资源,适合做业务相关的拦截和后置处理。”
你项目里现在主要用哪个做登录/鉴权?遇到过什么坑吗?可以继续聊聊具体场景。