过滤器和拦截器的区别是什么?
一则或许对你有用的小广告
欢迎 加入小哈的星球 ,你将获得: 专属的项目实战(已更新的所有项目都能学习) / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论
- 新开坑项目: 《Spring AI 项目实战(问答机器人、RAG 增强检索、联网搜索)》 正在持续爆肝中,基于
Spring AI + Spring Boot3.x + JDK 21..., 点击查看; - 《从零手撸:仿小红书(微服务架构)》 已完结,基于
Spring Cloud Alibaba + Spring Boot3.x + JDK 17..., 点击查看项目介绍; 演示链接: http://116.62.199.48:7070/; - 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/
面试考察点
面试官提出这个问题,主要想考察以下几个层面的理解:
- 对技术栈层次的清晰认知:你是否能清晰区分 Servlet 规范(Java EE/ Jakarta EE 标准)和 Spring 框架(具体实现)的边界。
- 对 Web 请求处理流程的深入理解:你是否了解一个 HTTP 请求到达应用后,经过的完整处理链条,以及不同组件在这个链条上的具体位置和作用。
- 对组件职责和适用场景的把握:不仅仅是记忆区别,更想知道你能否根据不同的业务需求(如安全、日志、性能监控)做出正确的技术选型。
- 对实现原理和扩展性的了解:你是否了解它们的底层实现机制(如动态代理),以及如何进行定制和扩展。
核心答案
过滤器 (Filter) 是 Servlet 规范的一部分,作用于 Web 容器 层面。它在请求进入 Servlet 之前和响应离开 Servlet 之后工作,主要对请求/响应的内容进行粗粒度的预处理和后处理,如编码设置、全局日志、跨域处理。
拦截器 (Interceptor) 是 Spring MVC 框架的一部分,作用于 Spring 上下文 层面。它在请求进入 DispatcherServlet 之后,在具体的 Controller 方法执行前、后以及渲染完视图后(afterCompletion)介入,主要对请求进行细粒度的、与业务逻辑更相关的处理,如权限校验、日志审计、性能监控。
核心区别一句话:过滤器是 Servlet 的 “门卫”,拦截器是 Spring MVC 的 “安检员”。
深度解析
原理/机制
过滤器 (Filter) 基于 Servlet 规范 的 Filter 接口,其工作完全由 Web 容器(如 Tomcat)管理。它通过 FilterChain 进行链式调用,与任何框架无关。
拦截器 (Interceptor) 基于 Spring AOP(面向切面编程) 和 动态代理 机制实现。它实现了 HandlerInterceptor 接口,由 Spring 的 DispatcherServlet 在内部流程中调用,因此天然可以获取到 Spring 上下文中的所有 Bean,并深度参与 MVC 流程。
代码示例
1. 过滤器实现示例
// 实现 javax.servlet.Filter (Jakarta EE 9+) 或 javax.servlet.Filter (Java EE 8)
@Component // 让 Spring 管理其生命周期,并通过 FilterRegistrationBean 注册
@Order(1) // 定义执行顺序
public class LoggingFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
long startTime = System.currentTimeMillis();
// 1. 在请求到达 Servlet 前执行 (预处理)
System.out.println("Filter: Request URI - " + httpRequest.getRequestURI());
chain.doFilter(request, response); // 放行,传递给下一个过滤器或最终的 Servlet
// 2. 在响应返回客户端前执行 (后处理)
long duration = System.currentTimeMillis() - startTime;
System.out.println("Filter: Request processed in " + duration + " ms");
}
}
2. 拦截器实现示例
// 实现 org.springframework.web.servlet.HandlerInterceptor
@Component
public class AuthInterceptor implements HandlerInterceptor {
// 在 Controller 方法执行前调用
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
String token = request.getHeader("Authorization");
if (!isValidToken(token)) {
response.sendError(HttpStatus.UNAUTHORIZED.value(), "Invalid token");
return false; // 中断执行链,不再调用后续拦截器和 Controller
}
return true; // 继续执行
}
// 在 Controller 方法执行后,视图渲染前调用
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
// 可以对 ModelAndView 进行修改
}
// 在整个请求完成(视图渲染完毕)后调用,常用于资源清理
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
// 记录请求完成,或处理异常
}
}
// 注册拦截器到 WebMvcConfigurer
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private AuthInterceptor authInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authInterceptor)
.addPathPatterns("/api/**") // 配置拦截路径
.excludePathPatterns("/api/public/**"); // 配置排除路径
}
}
对比分析与最佳实践
| 维度 | 过滤器 (Filter) | 拦截器 (Interceptor) |
|---|---|---|
| 归属与规范 | Servlet 规范 (J2EE/Jakarta EE) | Spring 框架 |
| 实现原理 | 基于 Servlet 容器的 FilterChain | 基于 Spring AOP 和动态代理 |
| 触发时机 | 最早 和 最晚。 在请求进入 Servlet 前,响应离开 Servlet 后。 | 在请求进入 DispatcherServlet 之后。preHandle (Controller 前), postHandle (Controller 后), afterCompletion (视图渲染后) |
| 作用范围 | 对所有进入容器的请求生效(包括静态资源) | 只对 Spring MVC 管理的请求生效(即经过 DispatcherServlet 的请求) |
| 获取上下文 | 只能获取 ServletContext | 可以获取 Spring 的 ApplicationContext 和所有 Bean |
| 处理对象 | ServletRequest 和 ServletResponse | HttpServletRequest, HttpServletResponse, HandlerMethod (可以获取方法、参数等元信息) |
| 异常处理 | 在 Filter 中抛出的异常,可以在 web.xml 或 @ControllerAdvice 之外 配置错误页面。 | 在 Interceptor 中抛出的异常,可以被 Spring 的 @ControllerAdvice 全局异常处理器 捕获。 |
最佳实践与选择依据:
- 使用过滤器:当你需要处理的是与具体业务无关的、全局性的、底层的请求/响应处理时。例如:
- 设置全局字符编码 (
CharacterEncodingFilter) - 跨域资源共享 (
CorsFilter) - 压缩响应内容 (
GzipFilter) - 防止 XSS 攻击的请求参数过滤
- 设置全局字符编码 (
- 使用拦截器:当你需要处理的是与业务逻辑紧密相关、依赖于 Spring 环境 的处理时。例如:
- 用户登录状态与权限校验
- 审计日志(记录谁在什么时间访问了哪个方法)
- 接口性能监控(精确到 Controller 方法级别)
- 通用的参数预处理或后处理
常见误区
- 认为过滤器是 Spring 特有的:这是一个常见误解。过滤器是 Java Web 标准,即使不使用 Spring,纯 Servlet 项目也能用。
- 不清楚执行顺序:在一个请求中,如果同时存在多个过滤器和拦截器,其执行顺序为:
Filter->DispatcherServlet->Interceptor.preHandle->Controller->Interceptor.postHandle-> 渲染视图 ->Interceptor.afterCompletion->Filter。 - 在过滤器中试图注入 Spring Bean 失败:如果 Filter 由容器(如 Tomcat)直接实例化,则
@Autowired会失效。解决方案是使用DelegatingFilterProxy(Spring Boot 默认配置)或让 Filter 本身成为一个 Spring Bean(如上面示例所示,并用FilterRegistrationBean注册)。
总结
过滤器是 Web 容器的标准组件,作用于更底层,适合处理全局性、协议相关的任务;拦截器是 Spring MVC 的框架组件,深度集成于 Spring 上下文,适合处理与业务逻辑密切相关的横切关注点。理解它们的差异和适用场景,是构建清晰、可维护的 Web 应用架构的基础。