SpringMVC拦截器怎么配置?拦截器配置详细步骤,常见问题解答
- 虚拟主机
- 2026-08-27
- 1
Spring MVC 拦截器(Interceptor)是 Spring Web MVC 框架提供的一种面向切面编程的轻量级机制,用于在处理器方法(Controller)调用前、调用后以及视图渲染完成后执行自定义逻辑,它的配置方式清晰、执行顺序可控、与业务解耦,是构建权限校验、日志记录、性能监控、请求审计等横切关注点的首选方案,相比 Filter(过滤器),拦截器深度集成 Spring 容器,可以载入任何 Spring Bean,因此在实际企业级项目中应用更广泛、更灵活。
拦截器的三种配置方式(基于 Spring MVC)
XML 方式最传统,适合老项目
在 spring-mvc.xml 或 dispatcher-servlet.xml 中使用 <mvc:interceptors>
<mvc:interceptors> <!-- 全局拦截器:拦截所有请求 --> <bean class="com.example.interceptor.AuthInterceptor"/> <!-- 路径拦截器:只拦截特定路径 --> <mvc:interceptor> <mvc:mapping path="/api/"/> <mvc:exclude-mapping path="/api/login"/> <bean class="com.example.interceptor.ApiInterceptor"/> </mvc:interceptor> </mvc:interceptors>
Java Config 方式推荐,类型安全
实现 WebMvcConfigurer 接口并重写 addInterceptors 方法:
@Configuration public class WebMvcConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new AuthInterceptor()) .addPathPatterns("/") .excludePathPatterns("/login", "/register", "/static/"); } }
注解驱动 + 自定义注解精准控制,适合细粒度鉴权
通过自定义注解配合拦截器,实现方法级别的权限控制:
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface RequiresPermission { String value(); }
在拦截器中通过 HandlerMethod 获取注解并校验,这种方式能够将权限逻辑从业务代码中完全剥离,维护性极佳。
拦截器核心方法详解(推荐直接复用的基类)
Spring MVC 提供了 HandlerInterceptor 接口,包含三个默认方法(Java 8+ 默认方法),建议继承 HandlerInterceptorAdapter(旧版)或直接实现接口:
- preHandle:在 Controller 方法执行前调用。返回 true 继续执行,返回 false 则中断请求,适合做登录校验、黑名单拦截、参数预处理。
- postHandle:Controller 执行后、视图渲染前调用,适合修改 ModelAndView、统一添加公共数据。
- afterCompletion:请求完成后回调(无论是否异常),适合清理资源、记录日志、统计耗时。
重要:若 preHandle 返回 false,则后续的 postHandle 和 afterCompletion 都不会执行,且同链路上已执行过 preHandle 的拦截器的 afterCompletion 会逆序执行。
执行顺序与常见误区(干货)
拦截器的执行顺序遵循责任链模式,但存在一个关键细节:
- 若配置了多个拦截器 A、B,则执行顺序为:A.preHandle → B.preHandle → Controller → B.postHandle → A.postHandle → 视图渲染 → B.afterCompletion → A.afterCompletion
- preHandle 顺序执行,postHandle 和 afterCompletion 逆序执行。
常见误区:
- 误区一:认为 postHandle 一定能执行,实际上如果 Controller 抛出异常,postHandle 不会执行,只有 afterCompletion 会执行。
- 误区二:拦截器只拦截 Controller 方法,不拦截静态资源,若需拦截静态资源,需要配置 <mvc:resources> 与拦截器路径同时覆盖,或者直接使用 Filter。
- 误区三:拦截器与过滤器混淆,Filter 属于 Servlet 规范,基于容器,拦截所有请求包括静态资源;拦截器属于 Spring MVC,只针对 DispatcherServlet 路由的处理器。权限校验优先用拦截器,字符编码、CORS 等基础过滤优先用 Filter。
实战:完整登录拦截器 + 性能监控(附代码)
以下代码展示一个可复用的登录校验与性能统计拦截器,同时解决异步请求时 afterCompletion 中获取用户信息可能失效的问题(通过 HandlerMethod 和 ThreadLocal 隔离):
public class AuthAndMonitorInterceptor implements HandlerInterceptor { private static final ThreadLocal<Long> START_TIME = new ThreadLocal<>(); @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 性能监控开始 START_TIME.set(System.currentTimeMillis()); // 身份校验逻辑 if (handler instanceof HandlerMethod) { HandlerMethod hm = (HandlerMethod) handler; // 检查类/方法上的 @RequiresPermission 注解 RequiresPermission perm = hm.getMethodAnnotation(RequiresPermission.class); if (perm != null && !checkPermission(request, perm.value())) { response.setStatus(403); response.setContentType("application/json;charset=UTF-8"); response.getWriter().write("{"code":403,"msg&qu
ot;:"无权限"}"); return false; // 中断请求,不再执行后续逻辑 } } // 如为登录接口或公开接口,放行 return true; } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { Long start = START_TIME.get(); if (start != null) { long cost = System.currentTimeMillis() - start; System.out.println(request.getRequestURI() + " 耗时: " + cost + "ms"); START_TIME.remove(); // 防止线程池复用导致数据错乱 } } private boolean checkPermission(HttpServletRequest request, String perm) { // 从 Session 或 Token 中解析权限,这里做简化演示 return request.getSession().getAttribute("user") != null; } }
配置该拦截器(Java Config):
registry.addInterceptor(new AuthAndMonitorInterceptor()) .addPathPatterns("/") .excludePathPatterns("/login", "/error", "/static/");
西西云经验案例:高并发场景下的拦截器线程安全实践
西西云在服务某金融级客户时遇到一个典型问题:拦截器中使用了非线程安全的成员变量来存储请求上下文,导致高并发下用户数据串号,我们当时给出的方案是结合西西云云服务器(SSD 高性能实例)进行压测验证,最终落地了以下实践:
- 禁止在拦截器中定义可变的成员变量,必须使用 ThreadLocal 或在方法栈内传递数据。
- 对于需要全局共享的只读配置(比如白名单路径列表),使用 volatile 修饰并只读初始化,避免并发失效。
- 利用西西云的弹性伸缩能力,在流量高峰前预置多台云服务器,同时利用缓存的 excludePathPatterns 配置热更新通过西西云对象存储下发配置,拦截器内定期拉取,实现免重启更新拦截规则。
- 经过压测,性能瓶颈不在拦截器,而在于数据库连接和日志同步写,于是我们将日志改为异步写入西西云日志服务,拦截器只负责采集,显著提升吞吐量。
核心观点:拦截器配置本身简单,但企业级应用必须关注并发安全、性能监控和可观测性,建议在拦截器内只做轻量级逻辑,杜绝数据库查询、远程调用等耗时操作,如有需要可结合消息队列异步化。
性能对比与最佳实践(E-E-A-T 经验总结)
| 方式 | 性能影响 | 使用场景 | 推荐度 |
|---|---|---|---|
| XML 配置 | 无额外开销,启动加载略慢 | 老项目维护 | |
| Java Config |
无额外开销,类型安全 | 新项目、Spring Boot 项目 | |
| 自定义注解拦截 | 反射少量开销 | 细粒度权限控制 | |
| 多个拦截器链 | 每个请求多几次方法调用 | 拆分为独立职责 |