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


面试考察点

  1. 架构理解:面试官不仅仅是想知道两者的定义,更是想看你能否说清楚它们在请求处理链中的位置关系,以及为什么 Spring 要在 Filter 之外再设计一套 Interceptor。

  2. 实战经验:是否在项目中真正用过,能否举出合适的场景说明什么时候该用 Filter、什么时候该用 Interceptor,而不是笼统地说 "都能做"。

  3. 原理深度:能否从 Servlet 容器、Spring IoC、Spring MVC 的 DispatcherServlet 等层面解释两者的本质差异。

核心答案

一句话先给结论:过滤器(Filter)是 Servlet 规范的一部分,由 Servlet 容器管理;拦截器(Interceptor)是 Spring MVC 框架的一部分,由 Spring 容器管理。 两者都能在请求到达 Controller 前后做处理,但作用范围、生命周期、能获取的信息量完全不同。

核心区别一览表:

对比维度Filter(过滤器)Interceptor(拦截器)
规范归属Servlet 规范Spring MVC 框架
管理容器Servlet 容器(Tomcat)Spring IoC 容器
作用范围所有请求(包括静态资源)仅 DispatcherServlet 处理的请求
触发时机在 DispatcherServlet 之前在 DispatcherServlet 之内,Handler 执行前后
能否获取 Handler 信息不能能(方法名、参数、注解等)
能否注入 Spring Bean默认不能(需额外配置)天然支持
典型场景编码转换、跨域、压缩、全局通用权限校验、日志、事务、业务相关

深度解析

一、请求处理链路

光看表格还不够直观,咱们来画一下一次 HTTP 请求的完整链路:

上图展示了一次完整请求的链路。整体可以分为三个层次来理解:

  • Filter 层(Servlet 容器层面):所有请求先进 Filter 链,Filter 不知道后面是 Spring 还是其他框架,它只认 Servlet。Filter 的执行顺序由注册顺序或 @Order 决定。

  • DispatcherServlet 层(Spring MVC 入口):请求穿过 Filter 链后到达 DispatcherServlet,从这里开始进入 Spring 的地盘。DispatcherServlet 负责根据 URL 找到对应的 Handler(即 Controller 方法)。

  • Interceptor 层(Spring MVC 框架层面):找到 Handler 之后、真正执行之前,先依次执行所有 Interceptor 的 preHandle() 方法;Controller 执行完毕后,逆序执行 postHandle();视图渲染完成后,再逆序执行 afterCompletion()

关键点在于:Filter 在 DispatcherServlet 外面,Interceptor 在里面。 这决定了它们能看到的、能做的事情完全不同。

二、代码实现对比

光说不练假把式,来两段代码感受下差异。

Filter 的实现:

@Component
public class AuthFilter implements Filter {

    // 注意:Filter 接口来自 javax.servlet 包
    // 不是 Spring 的接口,是 Servlet 规范定义的

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;

        // 前置处理
        String token = req.getHeader("Authorization");
        if (token == null) {
            // 这里拿不到 Handler 信息,不知道要调用哪个 Controller 方法
            ((HttpServletResponse) response).sendError(401, "未登录");
            return;
        }

        // 放行,交给下一个 Filter 或 Servlet
        chain.doFilter(request, response);

        // 后置处理(响应返回后)
        System.out.println("请求处理完毕");
    }
}

Interceptor 的实现:

@Component
public class AuthInterceptor implements HandlerInterceptor {

    // 注意:这个接口来自 org.springframework.web.servlet 包
    // 是 Spring MVC 专属的

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                             Object handler) throws Exception {
        // handler 就是目标 Controller 方法
        // 可以拿到方法名、参数、注解等所有信息!
        if (handler instanceof HandlerMethod) {
            HandlerMethod method = (HandlerMethod) handler;
            // 比如:检查方法上有没有某个自定义注解
            RequireLogin annotation = method.getMethodAnnotation(RequireLogin.class);
            if (annotation != null) {
                String token = request.getHeader("Authorization");
                if (token == null) {
                    response.sendError(401, "未登录");
                    return false; // 返回 false 就不继续往下走了
                }
            }
        }
        return true; // 放行
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response,
                           Object handler, ModelAndView modelAndView) {
        // Controller 执行后、视图渲染前
        // 可以修改 ModelAndView
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
                                Object handler, Exception ex) {
        // 视图渲染后、响应返回前
        // 适合做资源清理、异常日志记录
    }
}

看出区别了吧?Interceptor 能拿到 handler 对象,这意味着它知道你要调用哪个 Controller 的哪个方法,方法上有哪些注解,参数是什么。Filter 就没那么幸运了,它只知道有个请求来了,至于后面交给谁处理,它一无所知。

三、Spring Bean 注入的差异

这块很多人踩坑。Filter 默认是由 Servlet 容器管理的,不是 Spring Bean,所以直接用 @Autowired 注入是拿不到的(Spring Boot 注册的 Filter 除外)。

// ❌ 这样注入的 userService 可能为 null
public class MyFilter implements Filter {
    @Autowired
    private UserService userService; // null!
}

解决方案有几种:

  • Spring Boot 环境下FilterRegistrationBean 注册,或者直接加 @Component,Spring Boot 会自动处理
  • 传统 Spring MVC 环境下用 DelegatingFilterProxy 做桥接

而 Interceptor 就没这个问题,它本身就是 Spring 管理的 Bean,@Autowired 直接用,丝滑得很。

四、使用场景怎么选?

说个简单的判断标准:

用 Filter 的场景——和业务无关的通用处理:

  • 字符编码转换(CharacterEncodingFilter
  • CORS 跨域处理
  • GZIP 压缩
  • XSS 防护(全局的)
  • 请求日志记录(需要记录所有请求,包括静态资源的)

用 Interceptor 的场景——和业务相关的处理:

  • 登录状态校验(需要知道哪个接口需要登录)
  • 权限校验(需要知道方法上的权限注解)
  • 操作日志(需要知道调的是哪个方法、谁调的)
  • 事务管理
  • 接口限流(针对特定接口)

一句话总结就是:通用选 Filter,业务选 Interceptor。

面试高频追问

  1. 追问一:一个请求可以同时被 Filter 和 Interceptor 拦截吗?执行顺序是什么?

    当然可以。执行顺序是:Filter → DispatcherServlet → Interceptor.preHandle → Controller → Interceptor.postHandle → Interceptor.afterCompletion → Filter。Filter 永远在最外层。

  2. 追问二:Filter 链和 Interceptor 链的执行顺序怎么控制?

    Filter 通过 @Order 注解或 FilterRegistrationBeansetOrder() 控制顺序,值越小越先执行。Interceptor 通过 WebMvcConfigureraddInterceptors() 方法注册顺序决定,先注册的先执行 preHandle,后执行 postHandleafterCompletion(和 Filter 一样,是洋葱模型)。

  3. 追问三:Spring Boot 中 Filter 用 @Component 注册和用 FilterRegistrationBean 注册有什么区别?

    @Component 注册的 Filter 会拦截所有请求(/*),且无法精确控制顺序(虽然可以用 @Order 但语义不够明确)。FilterRegistrationBean 可以精确配置 URL 匹配模式、执行顺序,还能控制是否启用,生产环境更推荐后者。

常见面试变体

  • "Spring 中 Filter 和 Interceptor 有什么区别?各自适用什么场景?"
  • "Spring Boot 请求处理链路是怎样的?Filter、Interceptor、Controller 的执行顺序?"
  • "做一个登录校验,用 Filter 还是 Interceptor?为什么?"

记忆口诀

Filter 在外 Interceptor 在内,Filter 管 Servlet Interceptor 管 Handler,通用用 Filter 业务用 Interceptor。

记住三个关键差异:规范不同(Servlet vs Spring)、管理容器不同(Tomcat vs IoC)、能获取的信息不同(只知道有请求 vs 知道调哪个方法)。

总结

过滤器是 Servlet 规范的组件,由容器管理,适合做通用的、和框架无关的预处理;拦截器是 Spring MVC 的组件,由 Spring 管理,能获取 Handler 信息,适合做业务相关的处理。记住 "Filter 在 DispatcherServlet 外面,Interceptor 在里面" 这个核心位置关系,执行顺序和作用范围的差异就全通了。