人人都会AI编程

12.5 拦截器 HandlerInterceptor 与过滤器 Filter 对比与选型

更新时间:2026-07-11

在 Spring MVC 开发中,开发者常会遇到一个选择题:什么时候用过滤器(Filter),什么时候用拦截器(HandlerInterceptor)? 两者都能拦截请求并插入通用逻辑,但它们的运行机制、所处层次和适用场景有着显著区别。理解这些差异,才能避免滥用或误用。

12.5.1 概念与工作位置

过滤器(Filter)

Filter 是 Servlet 规范 定义的标准组件,工作在 Web 容器(如 Tomcat)级别。它直接拦截进入容器的 原始请求(HttpServletRequest),在所有请求到达 Servlet(DispatcherServlet)之前和之后执行。Filter 无法直接感知 Spring 容器中的 Bean,因为此时请求尚未进入 Spring 的管辖范围。

拦截器(HandlerInterceptor)

HandlerInterceptor 是 Spring MVC 框架 提供的组件,运行在 DispatcherServlet 内部。它拦截的是已经由 DispatcherServlet 分发的 处理器执行链,可以精准地在 Controller 方法执行前后、视图渲染前后插入逻辑。拦截器天然能够访问 Spring 的 IoC 容器,注入任何 Spring 管理的 Bean。

12.5.2 详细对比

| 维度 | 过滤器 (Filter) | 拦截器 (HandlerInterceptor) |
|------|----------------|----------------------------|
| 规范/来源 | Servlet 规范,属于 J2EE 标准 | Spring MVC 框架自有组件 |
| 运行容器 | Servlet 容器(Tomcat 等),脱离 Spring 也可工作 | Spring IoC 容器内部,必须运行在 Spring 环境中 |
| 执行时机 | 在请求进入 Servlet 之前、之后 | 在 DispatcherServlet 接收请求后,HandlerMapping 确定处理器后,Controller 方法执行前后,视图渲染前后 |
| 能访问的对象 | ServletRequest, ServletResponse,无法直接获取 Handler 信息 | HttpServletRequest, HttpServletResponse, HandlerMethod(具体的 Controller 方法),以及经过 @RequestBody 解析的参数等 |
| Spring Bean 注入 | 困难,需通过 WebApplicationContextUtils 间接获取,或通过代理类手动注入 | 直接支持,拦截器本身可通过 @Component 声明为 Bean,使用 @Autowired 注入任何依赖 |
| 细粒度控制 | 只能通过 URL 模式匹配(如 /api/*)决定过滤哪些请求 | 可通过 excludePathPatternsorder 精确控制,甚至可以基于注解或方法信息进行条件判断 |
| 处理范围 | 所有进入应用的请求,包括静态资源 | 默认只拦截进入 Controller 的请求,除非配置包含静态资源 |
| 依赖 Spring 特性 | 无法天然感知 Spring 的上下文和特性(如 AOP 增强) | 可以使用 Spring 的全部能力,包括事务、异常处理、SpEL 表达式等 |
| 执行链顺序 | 多个 Filter 通过 web.xml@Order 控制顺序 | 多个 Interceptor 通过 addInterceptor 顺序及 order 控制 |
| 生命周期 | 与容器生命周期一致,在容器启动时初始化 | 受 Spring 容器管理,默认与 ApplicationContext 生命周期一致 |

12.5.3 执行流程示意

结合一个典型的 Spring Boot 请求流程,可以清晰看到 Filter 和 Interceptor 的时序关系:

客户端请求
    ↓
[Filter 1]  →  [Filter 2]  →  ...  →  (所有 Filter 链)
    ↓
DispatcherServlet(前端控制器)
    ↓
[Interceptor preHandle] (拦截器前置处理)
    ↓
Controller 方法执行
    ↓
[Interceptor postHandle] (后置处理)
    ↓
视图解析 & 渲染
    ↓
[Interceptor afterCompletion] (完成处理)
    ↓
离开 DispatcherServlet
    ↓
回到 Filter 链的后处理逻辑
    ↓
响应返回客户端

Filter 在任何请求进入 DispatcherServlet 之前就已经生效,因此常见于字符编码设置、跨域处理(CORS)、安全认证(如 Spring Security 的 Filter 链)等场景。Interceptor 则更擅长与 Spring MVC 的处理器紧密协作,如权限注解检查、操作日志记录、全局用户信息注入等。

12.5.4 代码样例对比

Filter 示例

@WebFilter(urlPatterns = "/api/*")
public class LoggingFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpReq = (HttpServletRequest) request;
        System.out.println("Filter: 请求进入 -> " + httpReq.getRequestURI());
        chain.doFilter(request, response);
        System.out.println("Filter: 响应返回 <- " + httpReq.getRequestURI());
    }
}

Interceptor 示例

@Component
public class AuthInterceptor implements HandlerInterceptor {

    @Autowired
    private TokenService tokenService;  // 直接注入 Spring Bean

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                             Object handler) throws Exception {
        if (handler instanceof HandlerMethod) {
            // 可以获取到具体方法上的注解
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            RequireLogin annotation = handlerMethod.getMethodAnnotation(RequireLogin.class);
            if (annotation != null) {
                String token = request.getHeader("Authorization");
                return tokenService.validate(token);
            }
        }
        return true;
    }
}

配置拦截器时,可以精确排除静态资源或特定路径:

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Autowired
    private AuthInterceptor authInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(authInterceptor)
                .addPathPatterns("/api/**")
                .excludePathPatterns("/api/public/**", "/static/**");
    }
}

12.5.5 实战选型建议

根据多年项目经验,以下原则具有普适性:

优先选用 Interceptor 的场景:

  • 需要获取当前执行的具体 Controller 方法及其注解,进行权限校验(如 @PreAuthorize 的粗粒度替代)。
  • 需要在 Controller 方法执行前后注入统一的用户信息、语言区域等。
  • 需要记录请求日志,并且日志中要包含方法签名、参数绑定结果等 Spring MVC 特有信息。
  • 需要直接使用 Spring 管理的 Bean(如 Redis 缓存服务、用户权限服务)。
  • 需要针对某个业务注解做通用处理,例如 @Idempotent(幂等)、@RateLimit(限流)。

必须选用 Filter 的场景:

  • 字符编码设置(CharacterEncodingFilter),若在 Interceptor 阶段处理已太晚。
  • 跨域请求处理(CORS)的全局 Header 设置,Spring 4.2 后已有 CorsFilter 实现,通常在 Filter 层最干净。
  • Spring Security 的核心过滤器链,必须在请求进入 Spring MVC 前完成认证。
  • 请求体内容缓存(如 ContentCachingRequestWrapper),因为 Interceptor 阶段可能已经消费了输入流。
  • 与第三方 Servlet 组件集成,它们只认识 Filter 接口。

两者协同的典型模式:

一个微服务通常会同时使用 Filter 和 Interceptor。例如:

  • Filter 层完成 CORS 配置、请求 ID 生成注入、请求体缓存包装。
  • Interceptor 层完成用户认证信息解析、操作日志记录、统一返回格式包装。

这样的分层不仅职责清晰,而且不会因为跨层依赖导致混乱。此外,Filter 天然可以用于保护静态资源,而 Interceptor 默认不拦截静态资源(若启用了 spring.mvc.static-path-pattern 或静态资源映射,通常 Filter 层处理更稳妥)。

关键决策问句:

在犹豫时,可以问自己两个问题:

  1. 是否需要感知 Spring 容器中的 Bean 或具体的 Handler 信息? 如果需要,选 Interceptor。
  2. 这个逻辑是否必须在请求进入 DispatcherServlet 之前执行? 如果是(比如编码、安全链、请求体包装),选 Filter。

理解了 Filter 和 Interceptor 的边界,不仅能写出更清晰的代码,也能避免在项目中引入不必要的“万能组件”,使横向逻辑各安其位。