拦截器执行顺序
SpringMVC拦截器通过HandlerExecutionChain实现责任链模式,执行顺序严格遵循特定规则。
拦截器接口
Java
public interface HandlerInterceptor {
default boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
return true;
}
default void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
}
default void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler,
Exception ex) throws Exception {
}
}
执行顺序规则
| 方法 | 执行时机 | 执行顺序 | 说明 |
|---|---|---|---|
| preHandle | Handler执行前 | 正序(1→2→3) | 任一返回false则终止 |
| postHandle | Handler执行后 | 逆序(3→2→1) | 视图渲染前 |
| afterCompletion | 视图渲染后 | 逆序(3→2→1) | 始终执行(类似finally) |
源码实现
HandlerExecutionChain核心逻辑
Java
public class HandlerExecutionChain {
private final Object handler;
private final List<HandlerInterceptor> interceptorList = new ArrayList<>();
// preHandle - 正序执行
boolean applyPreHandle(HttpServletRequest request,
HttpServletResponse response) {
for (int i = 0; i < interceptorList.size(); i++) {
HandlerInterceptor interceptor = interceptorList.get(i);
if (!interceptor.preHandle(request, response, this.handler)) {
// 触发已执行拦截器的afterCompletion
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i; // 记录执行位置
}
return true;
}
// postHandle - 逆序执行
void applyPostHandle(HttpServletRequest request,
HttpServletResponse response, ModelAndView mv) {
for (int i = interceptorList.size() - 1; i >= 0; i--) {
HandlerInterceptor interceptor = interceptorList.get(i);
interceptor.postHandle(request, response, this.handler, mv);
}
}
// afterCompletion - 逆序执行
void triggerAfterCompletion(HttpServletRequest request,
HttpServletResponse response, Exception ex) {
// 只执行preHandle成功的拦截器
for (int i = this.interceptorIndex; i >= 0; i--) {
HandlerInterceptor interceptor = interceptorList.get(i);
interceptor.afterCompletion(request, response, this.handler, ex);
}
}
}
DispatcherServlet调用流程
Java
protected void doDispatch(HttpServletRequest request,
HttpServletResponse response) {
HandlerExecutionChain mappedHandler = null;
try {
ModelAndView mv = null;
// 1. 获取Handler执行链
mappedHandler = getHandler(processedRequest);
// 2. preHandle - 正序执行
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return; // 某个拦截器返回false,终止请求
}
// 3. 执行Handler
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 4. postHandle - 逆序执行
mappedHandler.applyPostHandle(processedRequest, response, mv);
// 5. 渲染视图
processDispatchResult(processedRequest, response, mappedHandler, mv, null);
} catch (Exception ex) {
// 异常时触发afterCompletion
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
}
执行顺序图解
Java
请求 → Interceptor1.preHandle
→ Interceptor2.preHandle
→ Interceptor3.preHandle
→ Handler执行
→ Interceptor3.postHandle
→ Interceptor2.postHandle
→ Interceptor1.postHandle
→ 视图渲染
→ Interceptor3.afterCompletion
→ Interceptor2.afterCompletion
→ Interceptor1.afterCompletion
→ 响应
异常场景处理
preHandle返回false
Java
// 假设Interceptor2返回false
请求 → Interceptor1.preHandle(true)
→ Interceptor2.preHandle(false)
→ Interceptor1.afterCompletion // 只执行已成功的
→ 响应(不进入Handler)
Handler抛出异常
Java
// Handler执行异常时
请求 → 所有preHandle(true)
→ Handler执行 → 异常!
→ postHandle不执行
→ 视图不渲染
→ 所有afterCompletion执行(逆序,带异常参数)
→ 异常处理
拦截器配置示例
text
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new AuthInterceptor())
.addPathPatterns("/**")
.order(1); // 顺序
registry.addInterceptor(new LogInterceptor())
.addPathPatterns("/**")
.order(2);
registry.addInterceptor(new CacheInterceptor())
.addPathPatterns("/**")
.order(3);
}
}
preHandle顺序决定拦截优先级,afterCompletion保证资源清理始终执行。
要点总结
- preHandle正序执行,任一返回false则终止链条
- postHandle逆序执行,视图渲染前处理
- afterCompletion逆序执行,类似finally保证资源清理
- interceptorIndex记录preHandle成功位置,用于afterCompletion范围控制
- 异常时postHandle不执行,但afterCompletion始终执行
jwdev/articles/SPRINGMVC/专家/MVC核心源码执行流程/MVC核心源码执行流程/拦截器执行顺序.md
📝 发现内容有误?点击此处直接编辑