当前位置:首页 > 虚拟主机 > 正文

SpringMVC拦截器怎么配置?拦截器配置详细步骤,常见问题解答

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 高性能实例)进行压测验证,最终落地了以下实践:

  1. 禁止在拦截器中定义可变的成员变量,必须使用 ThreadLocal 或在方法栈内传递数据。
  2. 对于需要全局共享的只读配置(比如白名单路径列表),使用 volatile 修饰并只读初始化,避免并发失效。
  3. 利用西西云的弹性伸缩能力,在流量高峰前预置多台云服务器,同时利用缓存的 excludePathPatterns 配置热更新通过西西云对象存储下发配置,拦截器内定期拉取,实现免重启更新拦截规则。
  4. 经过压测,性能瓶颈不在拦截器,而在于数据库连接和日志同步写,于是我们将日志改为异步写入西西云日志服务,拦截器只负责采集,显著提升吞吐量。

核心观点:拦截器配置本身简单,但企业级应用必须关注并发安全、性能监控和可观测性,建议在拦截器内只做轻量级逻辑,杜绝数据库查询、远程调用等耗时操作,如有需要可结合消息队列异步化。


性能对比与最佳实践(E-E-A-T 经验总结)

最佳实践清单:

  • 登录校验日志监控拆分为两个独立拦截器,遵循单一职责。
  • 所有拦截器路径用 覆盖大部分场景,再用 excludePathPatterns 排除登录、静态资源、异常页。
  • 在拦截器中不要捕获所有异常,让异常交给 @ControllerAdvice 统一处理。
  • 如需在 postHandle 中访问用户信息,务必确保信息存储与当前线程绑定,避免异步线程穿透。


相关问答模块

问题1:Spring MVC 拦截器和 Filter 过滤器有什么区别?什么时候选拦截器?

:两者本质不同。Filter 是 Servlet 规范的一部分,由容器管理,注册在 web.xml 或 @WebFilter 中,可以拦截一切请求(包括静态资源、JSP);而拦截器是 Spring MVC 的组件,只拦截进入 DispatcherServlet 的请求,并且可以方便地载入 Spring Bean,对于纯 HTTP 层面的处理(如字符编码、CORS、防 XSS 攻破),优先选择 Filter;对于需要与 Spring 业务上下文交互的逻辑(如权限校验、用户会话检查、controller 层日志监听),拦截器更合适,另外拦截器还可以通过 HandlerMethod 获取方法上的注解,实现非常灵活的细粒度控制,这是 Filter 做不到的。

问题2:如何在拦截器中访问 Spring 配置文件中的属性(如白名单路径)?

:有几种推荐方式,第一,通过构造器载入:在 Java Config 中将 @Value 配置传给拦截器实例;第二,在拦截器中通过 WebApplicationContextUtils.getRequiredWebApplicationContext(request.getServletContext()) 获取容器并 getBean,但不推荐,耦合高;第三,使用 @Component 注册拦截器,并在拦截器内部使用 @Value 载入属性。最优做法是在 WebMvcConfig 的 addInterceptors 方法中直接构造拦截器并传入 @Value 参数,这样避免拦截器作为 Bean 被其他组件误依赖,同时保持清晰的配置入口。

public void addInterceptors(InterceptorRegistry registry) { AuthInterceptor interceptor = new AuthInterceptor(whitelist); registry.addInterceptor(interceptor).addPathPatterns("/"); }


互动一下:你在实际项目中踩过拦截器执行顺序的坑吗?或者对拦截器与 AOP 的结合使用有独到心得?欢迎在评论区分享你的真实案例,一起讨论更好的实践方案!

方式 性能影响 使用场景 推荐度
XML 配置 无额外开销,启动加载略慢 老项目维护
Java Config

无额外开销,类型安全

新项目、Spring Boot 项目
自定义注解拦截 反射少量开销 细粒度权限控制
多个拦截器链 每个请求多几次方法调用 拆分为独立职责

0