过滤器和拦截器的区别是什么?

一则或许对你有用的小广告

欢迎 加入小哈的星球 ,你将获得: 专属的项目实战(已更新的所有项目都能学习) / 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/

面试考察点

面试官提出这个问题,主要想考察以下几个层面的理解:

  1. 对技术栈层次的清晰认知:你是否能清晰区分 Servlet 规范(Java EE/ Jakarta EE 标准)和 Spring 框架(具体实现)的边界。
  2. 对 Web 请求处理流程的深入理解:你是否了解一个 HTTP 请求到达应用后,经过的完整处理链条,以及不同组件在这个链条上的具体位置和作用。
  3. 对组件职责和适用场景的把握:不仅仅是记忆区别,更想知道你能否根据不同的业务需求(如安全、日志、性能监控)做出正确的技术选型。
  4. 对实现原理和扩展性的了解:你是否了解它们的底层实现机制(如动态代理),以及如何进行定制和扩展。

核心答案

过滤器 (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
处理对象ServletRequestServletResponseHttpServletRequest, HttpServletResponse, HandlerMethod (可以获取方法、参数等元信息)
异常处理Filter 中抛出的异常,可以在 web.xml 或 @ControllerAdvice 之外 配置错误页面。Interceptor 中抛出的异常,可以被 Spring 的 @ControllerAdvice 全局异常处理器 捕获。

最佳实践与选择依据

  • 使用过滤器:当你需要处理的是与具体业务无关的、全局性的、底层的请求/响应处理时。例如:
    • 设置全局字符编码 (CharacterEncodingFilter)
    • 跨域资源共享 (CorsFilter)
    • 压缩响应内容 (GzipFilter)
    • 防止 XSS 攻击的请求参数过滤
  • 使用拦截器:当你需要处理的是与业务逻辑紧密相关、依赖于 Spring 环境 的处理时。例如:
    • 用户登录状态与权限校验
    • 审计日志(记录谁在什么时间访问了哪个方法)
    • 接口性能监控(精确到 Controller 方法级别)
    • 通用的参数预处理或后处理

常见误区

  1. 认为过滤器是 Spring 特有的:这是一个常见误解。过滤器是 Java Web 标准,即使不使用 Spring,纯 Servlet 项目也能用。
  2. 不清楚执行顺序:在一个请求中,如果同时存在多个过滤器和拦截器,其执行顺序为:Filter -> DispatcherServlet -> Interceptor.preHandle -> Controller -> Interceptor.postHandle -> 渲染视图 -> Interceptor.afterCompletion -> Filter
  3. 在过滤器中试图注入 Spring Bean 失败:如果 Filter 由容器(如 Tomcat)直接实例化,则 @Autowired 会失效。解决方案是使用 DelegatingFilterProxy(Spring Boot 默认配置)或让 Filter 本身成为一个 Spring Bean(如上面示例所示,并用 FilterRegistrationBean 注册)。

总结

过滤器是 Web 容器的标准组件,作用于更底层,适合处理全局性、协议相关的任务;拦截器是 Spring MVC 的框架组件,深度集成于 Spring 上下文,适合处理与业务逻辑密切相关的横切关注点。理解它们的差异和适用场景,是构建清晰、可维护的 Web 应用架构的基础。