一、提出问题
- 项目中存在多个拦截器,那么他们的执行顺序是如何的?
- 如何设置拦截器执行顺序?
二、前期准备
项目结构:
主要代码如下,有拦截器 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)
方法设置顺序。
四、结论
根据实践和源码分析,当项目中存在多个拦截器,那么他们的执行顺序是根据方法而定的,拦截器所有的 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
方法执行顺序反之。