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 注册并声明拦截路径/排除路径。
架构师视角,拦截器是接入层治理的标准挂载点,我的三个高频用途:
- 统一鉴权与上下文:网关校验身份后,拦截器把 userId/tenantId 放入 ThreadLocal,业务代码随时可取、无需层层传参——记得 afterCompletion 清理,线程池复用会串数据;
- 租户/灰度标路由:读 Header 决定本次请求走哪个数据源或特性开关;
- 访问审计与接口耗时打点:公司级埋点在拦截器统一收口,业务零侵入。
治理要点:拦截器要做薄(重逻辑下沉到专门服务),路径规则集中在配置类管理;它与 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。