Spring MVC 拦截器详解:从原理到实践 拦截器(Interceptor)是 Spring MVC 提供的核心功能之一,用于在请求处理的不同阶段进行拦截并执行自定义逻辑(如权限校验、日志记录、性能监控等)。它与 Servlet 过滤器(Filter)类似,但更贴合 Spring MVC 的生命周期,使用更灵活。从 “拦截器与过滤器的区别→拦截器的使用→执行原理→实战场景” 全面解析 Spring MVC 拦截器。
拦截器与过滤器的核心区别 虽然拦截器和过滤器都能实现请求拦截,但两者在技术归属、执行时机、拦截范围 等方面有本质区别:
特性
拦截器(Interceptor)
过滤器(Filter)
技术归属
Spring MVC 框架提供
Servlet 规范提供(Java EE 原生)
执行时机
在 DispatcherServlet 内部执行
在 DispatcherServlet 之前执行
拦截范围
仅拦截 Spring MVC 管理的请求(如 @RequestMapping 标注的方法)
拦截所有进入 Web 容器的请求(包括静态资源、JSP 等)
底层实现
基于 Java 反射机制
基于函数回调
生命周期管理
由 Spring 容器管理,可注入 Spring Bean
由 Web 容器管理,无法直接使用 Spring 资源
方法粒度
可精确到 Controller 方法级别
仅能到 URL 级别
拦截器的核心接口与方法 Spring MVC 拦截器通过 HandlerInterceptor 接口定义核心行为,包含三个关键方法,分别对应请求处理的不同阶段:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public interface HandlerInterceptor { boolean preHandle (HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception; void postHandle (HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception; void afterCompletion (HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception; }
方法作用与典型场景
方法名
执行阶段
典型应用场景
preHandle
目标方法执行前
权限校验(如未登录则拦截)、日志记录(请求开始时间)、参数校验
postHandle
目标方法执行后,视图渲染前
修改模型数据(如统一添加全局参数)、替换视图
afterCompletion
视图渲染后
资源释放(如关闭文件流)、日志记录(请求结束时间,计算耗时)
拦截器的使用步骤 使用拦截器需经过 “实现接口→配置拦截规则 ” 两步,以下是详细流程:
步骤 1:实现拦截器接口 方式 1:直接实现 HandlerInterceptor 接口(推荐) 需重写所有三个方法(无需实现的方法可留空):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle (HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Object user = request.getSession().getAttribute("loginUser" ); if (user == null ) { response.sendRedirect(request.getContextPath() + "/login" ); return false ; } return true ; } @Override public void postHandle (HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { if (modelAndView != null ) { modelAndView.addObject("currentTime" , System.currentTimeMillis()); } } @Override public void afterCompletion (HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { Long startTime = (Long) request.getAttribute("startTime" ); if (startTime != null ) { long cost = System.currentTimeMillis() - startTime; System.out.println("请求 " + request.getRequestURI() + " 耗时:" + cost + "ms" ); } } }
方式 2:继承 HandlerInterceptorAdapter(已过时,不推荐) Spring 5.3 后该类已标记为过时,建议直接实现接口:
1 2 3 4 5 6 7 8 public class OldInterceptor extends HandlerInterceptorAdapter { @Override public boolean preHandle (HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return true ; } }
步骤 2:配置拦截器(XML 或 Java 配置) 方式 1:XML 配置(传统项目) 通过 <mvc:interceptors> 标签配置拦截器及拦截规则:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:mvc ="http://www.springframework.org/schema/mvc" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd" > <mvc:interceptors > <mvc:interceptor > <mvc:mapping path ="/**" /> <mvc:exclude-mapping path ="/login" /> <mvc:exclude-mapping path ="/static/**" /> <bean class ="com.example.interceptor.LoginInterceptor" /> </mvc:interceptor > <mvc:interceptor > <mvc:mapping path ="/admin/**" /> <bean class ="com.example.interceptor.AdminInterceptor" /> </mvc:interceptor > </mvc:interceptors > </beans >
方式 2:Java 配置(现代项目,推荐) 通过 WebMvcConfigurer 的 addInterceptors 方法配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import org.springframework.context.annotation.Configuration;import org.springframework.web.servlet.config.annotation.InterceptorRegistry;import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration public class WebMvcConfig implements WebMvcConfigurer { @Override public void addInterceptors (InterceptorRegistry registry) { registry.addInterceptor(new LoginInterceptor ()) .addPathPatterns("/**" ) .excludePathPatterns("/login" , "/static/**" ); registry.addInterceptor(new AdminInterceptor ()) .addPathPatterns("/admin/**" ); } }
拦截器的执行原理(结合源码) 拦截器的执行由 DispatcherServlet 主导,核心逻辑在 doDispatch 方法中。当请求到达 DispatcherServlet 后,会先通过 HandlerMapping 获取 HandlerExecutionChain(包含目标处理器和拦截器列表),再按顺序调用拦截器方法。
1. preHandle 方法:目标方法执行前调用(正序执行) DispatcherServlet 在调用目标方法前,通过 HandlerExecutionChain 的 applyPreHandle 方法执行所有拦截器的 preHandle:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 protected void doDispatch (HttpServletRequest request, HttpServletResponse response) throws Exception { HandlerExecutionChain mappedHandler = null ; try { mappedHandler = getHandler(processedRequest); if (mappedHandler == null ) { noHandlerFound(processedRequest, response); return ; } if (!mappedHandler.applyPreHandle(processedRequest, response)) { return ; } mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); } } boolean applyPreHandle (HttpServletRequest request, HttpServletResponse response) throws Exception { HandlerInterceptor[] interceptors = getInterceptors(); if (!ObjectUtils.isEmpty(interceptors)) { for (int i = 0 ; i < interceptors.length; i++) { HandlerInterceptor interceptor = interceptors[i]; if (!interceptor.preHandle(request, response, this .handler)) { triggerAfterCompletion(request, response, null ); return false ; } this .interceptorIndex = i; } } return true ; }
关键逻辑 :
拦截器按配置顺序(正序)执行 preHandle;
若某个拦截器返回 false,则触发 “已成功执行 preHandle 的拦截器” 的 afterCompletion 方法(反序),并终止后续流程。
2. postHandle 方法:目标方法执行后调用(反序执行) 目标方法执行完成后,DispatcherServlet 调用 HandlerExecutionChain 的 applyPostHandle 方法执行 postHandle:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 protected void doDispatch (HttpServletRequest request, HttpServletResponse response) throws Exception { if (mv != null && !mv.wasCleared()) { mappedHandler.applyPostHandle(processedRequest, response, mv); } } void applyPostHandle (HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception { HandlerInterceptor[] interceptors = getInterceptors(); if (!ObjectUtils.isEmpty(interceptors)) { for (int i = interceptors.length - 1 ; i >= 0 ; i--) { HandlerInterceptor interceptor = interceptors[i]; interceptor.postHandle(request, response, this .handler, mv); } } }
关键逻辑 :
拦截器按配置的反序执行 postHandle;
仅当所有拦截器的 preHandle 都返回 true 时,才会执行 postHandle。
3. afterCompletion 方法:视图渲染后调用(反序执行) 无论请求处理是否发生异常,afterCompletion 都会在视图渲染后执行:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 protected void doDispatch (HttpServletRequest request, HttpServletResponse response) throws Exception { HandlerExecutionChain mappedHandler = null ; try { } catch (Exception ex) { triggerAfterCompletion(processedRequest, response, mappedHandler, ex); } finally { if (mappedHandler != null ) { mappedHandler.triggerAfterCompletion(processedRequest, response, null ); } } } void triggerAfterCompletion (HttpServletRequest request, HttpServletResponse response, Exception ex) throws Exception { HandlerInterceptor[] interceptors = getInterceptors(); if (!ObjectUtils.isEmpty(interceptors)) { for (int i = this .interceptorIndex; i >= 0 ; i--) { HandlerInterceptor interceptor = interceptors[i]; try { interceptor.afterCompletion(request, response, this .handler, ex); } catch (Throwable ex2) { logger.error("拦截器 afterCompletion 执行异常" , ex2); } } } }
关键逻辑 :
仅执行 “preHandle 成功返回 true” 的拦截器的 afterCompletion;
按 preHandle 执行顺序的反序执行(与 postHandle 顺序一致)。
多个拦截器的执行顺序 当配置多个拦截器时,执行顺序遵循 “preHandle 正序,postHandle/afterCompletion 反序 ” 的规则。
示例:两个拦截器的执行流程 假设配置两个拦截器 InterceptorA 和 InterceptorB(A 在前,B 在后):
1 2 3 registry.addInterceptor(new InterceptorA ()).addPathPatterns("/**" ); registry.addInterceptor(new InterceptorB ()).addPathPatterns("/**" );
执行顺序如下:
1 2 3 4 5 6 7 8 1. InterceptorA.preHandle() → 返回 true 2. InterceptorB.preHandle() → 返回 true 3. 执行目标 Controller 方法 4. InterceptorB.postHandle() 5. InterceptorA.postHandle() 6. 视图渲染 7. InterceptorB.afterCompletion() 8. InterceptorA.afterCompletion()
特殊情况 :若 InterceptorB.preHandle() 返回 false:
1 2 3 1. InterceptorA.preHandle() → 返回 true 2. InterceptorB.preHandle() → 返回 false 3. InterceptorA.afterCompletion() → 仅执行已成功 preHandle 的拦截器
异步请求中的拦截器 对于异步请求(如返回 Callable、DeferredResult),Spring MVC 提供 AsyncHandlerInterceptor 接口(继承 HandlerInterceptor),新增 afterConcurrentHandlingStarted 方法:
1 2 3 4 5 6 7 8 public interface AsyncHandlerInterceptor extends HandlerInterceptor { default void afterConcurrentHandlingStarted (HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { } }
执行逻辑 :
异步请求处理时,preHandle 正常执行;
主线程会跳过 postHandle 和 afterCompletion,直接调用 afterConcurrentHandlingStarted;
异步任务完成后,若需要后续处理,需通过 AsyncWebRequest 注册回调。
拦截器的典型应用场景
权限校验 :在 preHandle 中检查用户是否登录或拥有操作权限(如示例中的 LoginInterceptor);
日志记录 :在 preHandle 记录请求开始时间,在 afterCompletion 记录结束时间,计算接口耗时;
参数统一处理 :在 preHandle 中对请求参数进行加密 / 解密、XSS 过滤等;
全局数据添加 :在 postHandle 中向 ModelAndView 添加全局参数(如当前登录用户信息、系统配置);
资源释放 :在 afterCompletion 中关闭文件流、释放数据库连接等。
总结 Spring MVC 拦截器是基于 HandlerInterceptor 接口的请求拦截机制,核心特点如下:
三个核心方法 :preHandle(前置处理)、postHandle(后置处理)、afterCompletion(完成处理),分别对应请求处理的不同阶段;
执行顺序 :多个拦截器时,preHandle 正序执行,postHandle 和 afterCompletion 反序执行;
与过滤器的区别 :拦截器属于 Spring MVC,在 DispatcherServlet 内部执行,可精确到方法级别,且能使用 Spring 资源;
灵活配置 :支持 XML 和 Java 两种配置方式,可通过 addPathPatterns 和 excludePathPatterns 精确控制拦截范围