Skip to content

Spring MVC 中的拦截器是什么?如何定义一个拦截器? ​

  • 标签:后端、Spring

🏛 业务架构师 ​

拦截器(HandlerInterceptor)是 Spring MVC 提供的请求处理链路上的横切组件,在 Handler 执行前后介入。定义方式:

java
@Component
public class AuthInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest req,
                             HttpServletResponse resp, Object handler) {
        String token = req.getHeader("Authorization");
        UserContext.set(parseUser(token));   // 鉴权 + 注入用户上下文
        return true;                          // false 则中断请求
    }

    @Override
    public void afterCompletion(...) {
        UserContext.clear();                  // 清理 ThreadLocal,防串号
    }
}

再通过 WebMvcConfigurer#addInterceptors 注册并声明拦截路径/排除路径。

架构师视角,拦截器是接入层治理的标准挂载点,我的三个高频用途:

  1. 统一鉴权与上下文:网关校验身份后,拦截器把 userId/tenantId 放入 ThreadLocal,业务代码随时可取、无需层层传参——记得 afterCompletion 清理,线程池复用会串数据;
  2. 租户/灰度标路由:读 Header 决定本次请求走哪个数据源或特性开关;
  3. 访问审计与接口耗时打点:公司级埋点在拦截器统一收口,业务零侵入。

治理要点:拦截器要做薄(重逻辑下沉到专门服务),路径规则集中在配置类管理;它与 Filter 的分工(容器级 vs MVC 级)、以及和网关层职责的切分(认证放网关、业务上下文放拦截器)是架构评审的常规话题。

🏗 系统架构师 ​

定义:实现 HandlerInterceptor(preHandle/postHandle/afterCompletion),通过 WebMvcConfigurer 的 addInterceptors 注册,可配路径与顺序。

java
@Component
public class TraceInterceptor implements HandlerInterceptor {
    public boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler) {
        MDC.put("traceId", req.getHeader("X-Trace-Id"));
        return true;
    }
    public void afterCompletion(HttpServletRequest r, HttpServletResponse p, Object h, Exception e) {
        MDC.clear();
    }
}

架构定位:Web 层横切的标准挂载点——登录态、权限、租户上下文、traceId、接口审计。我的分层原则:

  • Filter:协议层(编码、CORS、安全头),不依赖 Spring MVC;
  • Interceptor:业务路由层(认证、上下文),拿得到 HandlerMethod 元数据;
  • AOP:Service 层(事务、监控);
  • 网关:跨服务统一(限流、熔断、头清洗),先于应用内一切。

preHandle 要轻(token 校验走本地缓存),afterCompletion 必须清 ThreadLocal——线程池复用导致串号是经典事故。

💻 全栈开发专家 ​

HandlerInterceptor 是 MVC 的处理器拦截器,三个回调:

java
@Component
public class AuthInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler) {
        Long uid = jwtVerifier.verify(req.getHeader("Authorization"));
        UserContext.set(uid);                 // ThreadLocal 放登录态
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest req, HttpServletResponse resp,
                                Object handler, Exception ex) {
        UserContext.remove();                 // 铁律:线程复用,不清就串号
    }
}

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(authInterceptor)
                .addPathPatterns("/api/**")
                .excludePathPatterns("/api/login", "/actuator/**")
                .order(0);
    }
}

两条实战铁律:afterCompletion 里必须清 ThreadLocal——Tomcat 线程复用,不清就脏读串号,我排查过用户 A 看到 B 数据的事故,根因就是它;异常时 afterCompletion 依然执行(finally 语义),postHandle 不执行。

适用:登录态/租户注入、权限、审计日志。与 Filter 的分工:跨域、编码、请求体包装放 Filter(Servlet 层);要拿 handler 方法信息(读 HandlerMethod 上的注解做权限标记)放 Interceptor。