一、提出问题
- 项目中存在多个拦截器,那么他们的执行顺序是如何的?
- 如何设置拦截器执行顺序?
二、前期准备
项目结构:
主要代码如下,有拦截器 A、B、C,代码基本与下一致:
/** * 拦截器 A * * @author ouyang * @version 1.0 * @date 2020/7/30 15:18 **/ public class AInterceptor implements HandlerInterceptor { private final Logger logger = LoggerFactory.getLogger(AInterceptor.class); @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { logger.info("拦截器 A preHandle 方法执行"); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception { logger.info("拦截器 A postHandle 方法执行"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception { logger.info("拦截器 A afterCompletion 方法执行"); } }
配置拦截器类:
/** * 拦截器配置 * @author ouyang * @version 1.0 * @date 2020/7/30 15:19 **/ @Configuration public class InterceptorConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new AInterceptor()) .addPathPatterns("/**"); registry.addInterceptor(new BInterceptor()) .addPathPatterns("/**"); registry.addInterceptor(new CInterceptor()) .addPathPatterns("/**"); } }
三、拦截器实际运行结果
首先启动项目,任意访问一个接口,控制台打印如下:
2020-07-31 21:43:55.891 INFO 4284 --- [nio-8080-exec-1] com.study.aop.AInterceptor : 拦截器 A preHandle 方法执行 2020-07-31 21:43:55.891 INFO 4284 --- [nio-8080-exec-1] com.study.aop.BInterceptor : 拦截器 B preHandle 方法执行 2020-07-31 21:43:55.891 INFO 4284 --- [nio-8080-exec-1] com.study.aop.CInterceptor : 拦截器 C preHandle 方法执行 2020-07-31 21:43:55.936 INFO 4284 --- [nio-8080-exec-1] com.study.aop.CInterceptor : 拦截器 C postHandle 方法执行 2020-07-31 21:43:55.936 INFO 4284 --- [nio-8080-exec-1] com.study.aop.BInterceptor : 拦截器 B postHandle 方法执行 2020-07-31 21:43:55.936 INFO 4284 --- [nio-8080-exec-1] com.study.aop.AInterceptor : 拦截器 A postHandle 方法执行 2020-07-31 21:43:55.936 INFO 4284 --- [nio-8080-exec-1] com.study.aop.CInterceptor : 拦截器 C afterCompletion 方法执行 2020-07-31 21:43:55.936 INFO 4284 --- [nio-8080-exec-1] com.study.aop.BInterceptor : 拦截器 B afterCompletion 方法执行 2020-07-31 21:43:55.936 INFO 4284 --- [nio-8080-exec-1] com.study.aop.AInterceptor : 拦截器 A afterCompletion 方法执行
从中可以看出,拦截器 A,B,C 中
preHandle
执行顺序与registry.addInterceptor()
一致,postHandle
和afterCompletion
反之。
四、源码分析拦截器顺序
接下来我们跟踪一下源码,分析为什么会是上述现象:
首先 WebMvcConfigurationSupport 类中 requestMappingHandlerMapping 方法会将配置好的拦截器通过 InterceptorRegistry 类中 getInterceptors() 方法加载到 RequestMappingHandlerMapping;另外在请求时,通过 DispatcherServlet 中的 doDispatch 方法进行调用拦截器如下是 doDispatch 方法:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; boolean multipartRequestParsed = false; WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try { try { ModelAndView mv = null; Object dispatchException = null; try { processedRequest = this.checkMultipart(request); multipartRequestParsed = processedRequest != request; mappedHandler = this.getHandler(processedRequest); if (mappedHandler == null) { this.noHandlerFound(processedRequest, response); return; } HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler()); String method = request.getMethod(); boolean isGet = "GET".equals(method); if (isGet || "HEAD".equals(method)) { long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) { return; } } // 执行拦截器的全部 preHandle 方法 if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return; } this.applyDefaultViewName(processedRequest, mv); // 执行拦截器的全部 postHandle 方法 mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception var20) { dispatchException = var20; } catch (Throwable var21) { dispatchException = new NestedServletException("Handler dispatch failed", var21); } // 执行拦截器的全部 afterCompletion 方法 this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException); } catch (Exception var22) { // 异常后,执行拦截器的全部 afterCompletion 方法 this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22); } catch (Throwable var23) { // 异常后,执行拦截器的全部 afterCompletion 方法 this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23)); } } finally { if (asyncManager.isConcurrentHandlingStarted()) { if (mappedHandler != null) { mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); } } else if (multipartRequestParsed) { this.cleanupMultipart(processedRequest); } } }
首先我们先来看
mappedHandler.applyPreHandle()
方法实际方法:boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception { HandlerInterceptor[] interceptors = this.getInterceptors(); if (!ObjectUtils.isEmpty(interceptors)) { for(int i = 0; i < interceptors.length; this.interceptorIndex = i++) { HandlerInterceptor interceptor = interceptors[i]; if (!interceptor.preHandle(request, response, this.handler)) { this.triggerAfterCompletion(request, response, (Exception)null); return false; } } } return true; }
我们可以看出 applyPreHandle 是按照 HandlerInterceptor[] interceptors = this.getInterceptors(); 顺序执行的,也就是说 默认(不设定拦截器顺序) 是按照 registry.addInterceptor() 的顺序执行的;
接下来我们看 mappedHandler.applyPostHandle() ,实际执行方法如下:void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception { HandlerInterceptor[] interceptors = this.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); } } }
可以看出是与
applyPreHandle
正好相反的顺序执行的;mappedHandler.triggerAfterCompletion()
方法如下所示:void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) throws Exception { HandlerInterceptor[] interceptors = this.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 var8) { logger.error("HandlerInterceptor.afterCompletion threw exception", var8); } } } }
可以看出和
applyPostHandle
方法一致。最后我们来看一下
InterceptorRegistry
类的addInterceptor
和getInterceptors
方法,源码如下:public class InterceptorRegistry { private final List<InterceptorRegistration> registrations = new ArrayList(); private static final Comparator<Object> INTERCEPTOR_ORDER_COMPARATOR; public InterceptorRegistry() { } public InterceptorRegistration addInterceptor(HandlerInterceptor interceptor) { InterceptorRegistration registration = new InterceptorRegistration(interceptor); this.registrations.add(registration); return registration; } public InterceptorRegistration addWebRequestInterceptor(WebRequestInterceptor interceptor) { WebRequestHandlerInterceptorAdapter adapted = new WebRequestHandlerInterceptorAdapter(interceptor); InterceptorRegistration registration = new InterceptorRegistration(adapted); this.registrations.add(registration); return registration; } protected List<Object> getInterceptors() { return (List)this.registrations.stream().sorted(INTERCEPTOR_ORDER_COMPARATOR).map(InterceptorRegistration::getInterceptor).collect(Collectors.toList()); } static { INTERCEPTOR_ORDER_COMPARATOR = OrderComparator.INSTANCE.withSourceProvider((object) -> { if (object instanceof InterceptorRegistration) { InterceptorRegistration var10000 = (InterceptorRegistration)object; ((InterceptorRegistration)object).getClass(); return var10000::getOrder; } else { return null; } }); } }
不难看出, addInterceptor 方法是将拦截器放到 List<InterceptorRegistration> registrations = new ArrayList(); 中,另外需要特别注意 getInterceptors 方法,有代码 this.registrations.stream().sorted(INTERCEPTOR_ORDER_COMPARATOR),我们也可以看出 registrations 按照 order 升序排序,其中 InterceptorRegistration 类默认的 private int order = 0; ,可以通过 order(int order) 方法设置顺序。
5、总结
根据实践和源码分析,当项目中存在多个拦截器,那么他们的执行顺序是根据方法而定的,拦截器所有的 preHandle 方法是按照拦截器的 order 升序执行的,如果 order 一致,则按照添加顺序执行,postHandle 和 afterCompletion 方法执行顺序反之。
另外我们可以通过使用 order(int order) 设定执行的先后顺序,如下代码所示。@Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new AInterceptor()) .addPathPatterns("/**") .order(3); registry.addInterceptor(new BInterceptor()) .addPathPatterns("/**") .order(2); registry.addInterceptor(new CInterceptor()) .addPathPatterns("/**") .order(1); }
综上所述,默认情况下(不设定拦截器顺序),preHandle 方法执行顺序与拦截器注册时顺序一致,postHandle 和 afterCompletion 方法执行顺序反之;如果我们给每个拦截器设定 order ,则 preHandle 方法执行顺序是按照 order 升序执行,postHandle 和 afterCompletion 方法执行顺序反之。