【SpringMVC】浅看HandlerInterceptor源码

276 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 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映射来调整作用范围.