携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第9天,点击查看活动详情
浅看HandlerInterceptor源码
前情提要
在SpringMVC中, HandlerInterceptor是一个接口, 会借助dispatcherServlet的doDispatch方法来拦截指定请求的handler方法, 该接口提供了三个方法, 用来在handler执行前, 执行后, 完成后执行, 所以HandlerInterceptor可以用来拦截请求, 或者给特定handler方法增加额外功能.
使用idea看HandlerInterceptor源码
handlerInterceptor被封装到handlerExecutionChain.
先进入到dispatcherServlet的doDispatch方法中, 在490行会获得适配器, 根据HandlerMapping匹配到的handler获得适配器, 在还这里没有执行到prehandler.
HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
而504行已经执行了handler:
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
500行这段在handler方法调用之前执行. 我们在这里定位到了preHandle方法的执行.
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
在if中,如果为true, 就执行return, 中断流程, 对应着prehandler的返回值为false的情况.
说明HandlerExecutionChain.applyPreHandle()的返回值为false才会中断流程, 要看applyPreHandle什么时候返回false.
进入其源码,
会先获得interceptors数组, (this.getInterceptors()), 这多个handlerInterceptor是根据所有interceptor的作用范围来获得, 接下来做数组的非空判断, 如果不为空, 就遍历interceptor数组, 这里正序遍历每个interceptor, 执行当前handlerInterceptor的prehandle()方法, 返回值为false时, 就会执行triggerAfterCompletion方法并返回false
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;
}
}
先执行afterCompletion. 说明只要有一个HandlerInterceptor的prehandle方法返回值是false时, 就会直接调用afterCompletion, 并且return false, 使得HandlerExecutionChain.applyPreHandle()的返回值为false, 导致中断流程.
而如果所有HandlerInterceptor的prehandle方法返回值是true时, 会继续这个流程.
继续仔细分析, preHandle返回值为false, 执行的是哪里的afterCompletion?
继续看triggerAfterCompletion()的源码,
仍旧先获得interceptor数组, 做非空判断, 然后遍历interceptor数组, 注意这里是倒序遍历, 而且是从interceptorIndex开始做的倒序遍历, 这里的interceptorIndex是一个倒叙遍历开始的标记, 是在handlerExcutionChain中定义了InterceptorIndex标记, 在执行prehandle遍历的过程中做下了标记(可以看上一个代码块中有interceptorIndex), 它标记了哪些HandlerInterceptor的preHandle返回值为ture, 或者可以理解为它是最后一个返回值为ture的HandlerInterceptor在数组中的下标.
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);
}
}
所以如果所有的HandlerInterceptor的prehandle方法执行为true, 在倒叙遍历时会执行所有的AfterCompletion.
那么posthandle方法在哪里执行?
回到doDispatch方法
510行, 在执行完ha.handle()方法后, 才执行:
mappedHandler.applyPostHandle(processedRequest, response, mv);
作用是用来调整handler方法的执行结果, 点击源码, 仍旧遍历Interceptor数组, 而且是倒叙遍历全部的interceptor
for(int i = interceptors.length - 1; i >= 0; --i) {
HandlerInterceptor interceptor = interceptors[i];
interceptor.postHandle(request, response, this.handler, mv);
}
注意这里的遍历开始结点为数组末尾, 表示一旦遍历, 会遍历数组中所有interceptor, 但如果有interceptor的preHandle方法返回值为false, handler方法和post方法全部都不执行(因为会直接在prehandle方法中中断流程, 执行不到这行代码).
总结
整个interceptor中的方法的执行流程非常像代理, 相当于对handler方法做了增强, 但更加灵活, 可以更方便的给定请求url映射来调整作用范围.