
在基于Spring MVC框架开发Web应用时,我们经常使用拦截器(Interceptor)来处理诸如接口鉴权、请求日志记录、全局参数校验等通用逻辑。你可能已经熟悉其基本用法,但你是否深入理解其背后的执行顺序?为何preHandle返回false就会中断整个请求流程?而postHandle与afterCompletion的逆序执行又体现了怎样的设计哲学?本文将从DispatcherServlet的核心源码出发,为你揭开HandlerInterceptor的执行奥秘。
一、拦截器的“入口”:DispatcherServlet的doDispatch方法
所有拦截器的执行逻辑,都起始于DispatcherServlet的doDispatch方法,这是Spring MVC处理HTTP请求的核心调度方法。以下是其简化后的关键源码:
// DispatcherServlet.java
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
try {
// 1. 获取HandlerExecutionChain(包含目标Controller和拦截器链)
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// 2. 按序执行所有拦截器的preHandle方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return; // 任一preHandle返回false,则中断后续所有流程
}
// 3. 实际执行Controller中的处理方法
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 4. 逆序执行所有拦截器的postHandle方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
} catch (Exception ex) {
// 异常处理逻辑
} finally {
// 5. 最终触发已成功执行preHandle的拦截器的afterCompletion方法
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(processedRequest, response, ex);
}
}
}
这里的核心对象是mappedHandler,它是HandlerExecutionChain类的一个实例。这个“执行链”对象封装了两部分内容:最终要执行的目标Controller(Handler),以及为此请求配置的所有拦截器(HandlerInterceptor)列表。整个请求的拦截器生命周期,都由此链上的三个核心方法驱动。
二、拦截器的“执行规则”:正序与逆序的巧妙设计
HandlerExecutionChain中定义了三个核心方法,分别对应拦截器的三个生命周期回调。
1. preHandle:正序执行,扮演“守门员”
preHandle是拦截器的前置处理方法,常用于进行访问权限校验、请求参数预检等。我们来看applyPreHandle方法的源码实现:
// HandlerExecutionChain.java
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 正序遍历拦截器列表
for (int i = 0; i < this.interceptors.size(); i++) {
HandlerInterceptor interceptor = this.interceptors.get(i);
// 执行当前拦截器的preHandle
if (!interceptor.preHandle(request, response, this.handler)) {
// 如果返回false,则触发已执行拦截器的afterCompletion(逆序)
triggerAfterCompletion(request, response, null);
return false;
}
// 记录最后一个成功执行preHandle的拦截器索引
this.interceptorIndex = i;
}
return true;
}
关键点解析:
- 正序执行:拦截器按照配置文件的顺序依次执行(例如配置了A、B、C,则执行顺序为A→B→C)。
- 中断机制:若任意拦截器的
preHandle返回false,则立即终止流程,不会执行后续拦截器的preHandle,更不会执行目标Controller。同时,会逆序触发已成功执行了preHandle的那些拦截器的afterCompletion方法,用于资源清理。
2. postHandle:逆序执行,进行“后置增强”
postHandle在Controller方法执行之后、视图渲染之前被调用,可用于对ModelAndView进行修改,例如添加全局的模型数据。这在现代的SpringBoot Web应用实践中依然常见。其源码如下:
// HandlerExecutionChain.java
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception {
// 逆序遍历拦截器列表
for (int i = this.interceptors.size() - 1; i >= 0; i--) {
HandlerInterceptor interceptor = this.interceptors.get(i);
interceptor.postHandle(request, response, this.handler, mv);
}
}
设计思考:为何要逆序?
假设拦截器执行顺序是A→B→C(A最先,C最后)。postHandle的逆序(C→B→A)保证了最内层(最后执行的,如C)的拦截器先对处理结果进行增强,然后逐层向外传递。这种设计与“装饰器模式”或“栈”的结构类似,确保了处理逻辑的层次性和一致性,是后端架构中一种常见的责任链处理思想。
3. afterCompletion:逆序执行,完成“最终清理”
afterCompletion在整个请求结束时(视图渲染完毕后或发生异常时)被调用,适用于进行资源释放、性能监控记录等收尾工作。该方法无论请求处理成功或异常都会执行。
// HandlerExecutionChain.java
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) throws Exception {
// 从最后一个成功执行preHandle的拦截器开始,逆序执行
for (int i = this.interceptorIndex; i >= 0; i--) {
HandlerInterceptor interceptor = this.interceptors.get(i);
try {
interceptor.afterCompletion(request, response, this.handler, ex);
} catch (Throwable ex2) {
logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
}
}
}
特别注意:这里的循环变量i是从this.interceptorIndex开始的。该索引记录了在preHandle阶段最后一个成功执行的拦截器位置。因此,afterCompletion只会对那些preHandle执行成功的拦截器进行回调,并且执行顺序与postHandle相同,也是逆序的。
三、从UML时序图纵览全局流程
为了更清晰地把握整个交互过程,我们可以通过下面的UML时序图来可视化请求在拦截器链中的完整生命周期:

时序图关键节点解读:
DispatcherServlet接收到客户端请求后,首先通过getHandler方法获取封装好的HandlerExecutionChain。
- 调用链的
applyPreHandle方法,正序执行各拦截器的preHandle。若中途返回false,则直接跳至triggerAfterCompletion进行逆序清理。
Controller的业务方法被调用执行。
- Controller执行完毕后,调用链的
applyPostHandle方法,逆序执行各拦截器的postHandle。
- 最终,在
finally块中,调用链的triggerAfterCompletion方法,逆序执行已成功通过preHandle的拦截器的afterCompletion。
总结
Spring MVC拦截器的执行逻辑清晰体现了框架对请求生命周期的精细管理:preHandle是进入时的“安全检查”,postHandle是业务处理后的“增强修饰”,afterCompletion是离开时的“资源清理”。其“正序预处理,逆序后处理”的设计,既保证了逻辑的层层递进,也确保了资源释放的顺序安全。理解这一源码级细节,有助于我们在开发中更准确、更高效地运用拦截器这一强大组件,避免因执行顺序误解而导致的业务逻辑错误。