最近在使用Spring时,总感觉对HandlerInterceptor有点模糊,回头再来看看,记录下。
SpringFramework的版本4.3.x.RELEASE.
List-1
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; boolean multipartRequestParsed = false; WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try { ModelAndView mv = null; Exception dispatchException = null; try { processedRequest = checkMultipart(request); multipartRequestParsed = (processedRequest != request); // Determine handler for the current request. //1、得到handlerExecutionChain,里面含有对应的Controller,里面还有interceptor, mappedHandler = getHandler(processedRequest); if (mappedHandler == null || mappedHandler.getHandler() == null) { noHandlerFound(processedRequest, response); return; } // Determine handler adapter for the current request. //2、会根据Controller方法上的注解,将request中的请求转换为对象,转换为对应的pathVariable、body等 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // Process last-modified header, if supported by the handler. String method = request.getMethod(); boolean isGet = "GET".equals(method); if (isGet || "HEAD".equals(method)) { long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); if (logger.isDebugEnabled()) { logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified); } if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { return; } } //3、 if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } // Actually invoke the handler. //4、真正进行反射操作,调用方法 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return; } applyDefaultViewName(processedRequest, mv); //5、 mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception ex) { dispatchException = ex; } catch (Throwable err) { // As of 4.3, we're processing Errors thrown from handler methods as well, // making them available for @ExceptionHandler methods and other scenarios. dispatchException = new NestedServletException("Handler dispatch failed", err); } //6 processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } catch (Exception ex) { //7 triggerAfterCompletion(processedRequest, response, mappedHandler, ex); } catch (Throwable err) { //8 triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", err)); }
如List-1所示,
- 1位置,根据ServletRequest得到HandlerExecutionChain(具体怎么得到的有点复杂,后续再深入分析),它有点类似SpringSecurity中的FilterChainProxy。里面有HandlerInterceptor数组,和handler属性(通过debug可以看到是HandlerMethod)。
- 2位置,会根据Controller方法上的注解,将request中的请求转换为对象,转换为对应的pathVariable、body等。
- 3位置,调用HandlerExecutionChain的applyPreHandle,如下,逐个调用preHandle方法,只要preHandle返还false,就会你像调用afterCompletion,最后返还false。思考,通过源码可知在HandlerInterceptor中拦截后续preHandle中抛出的异常是拦截不到的。这个HandlerExceptionChain是线程安全的,每个请求对应一个HandlerExceptionChain实例对象,所以源码中通过类属性interceptorIndex来控制下标。
List-2
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception { HandlerInterceptor[] interceptors = getInterceptors(); if (!ObjectUtils.isEmpty(interceptors)) { for (int i = 0; i < interceptors.length; i++) { HandlerInterceptor interceptor = interceptors[i]; if (!interceptor.preHandle(request, response, this.handler)) { triggerAfterCompletion(request, response, null); return false; } this.interceptorIndex = i; } } return true;}
- 位置4,到位置4,已经执行了所有handlerInterceptor的preHandle,并且已经将ServletRequest中的参数转换为了Controller方法上标有注解的参数。所以在位置通过反射调用Controller的方法。通过源码可以看出,HandlerInterceptor可以看出,只要Controller里面的业务代码抛出异常,那么位置5就执行不到,就不会执行HandlerInterceptor的postHandle方法。
- 位置5,如下List-3所示,会逆向执行HandlerInterceptor的postHandle方法。
List-3
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception { HandlerInterceptor[] interceptors = 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); } }}
- 位置6、如果之前没有抛出异常,就调用processDispatchResult方法,方法processDispatchResult的最后,有如下List-4,调用HandlerExecutionChain的triggerAfterCompletion,会逆序调用HandlerInterceptor的afterCompletion,由List-5可以看出,afterCompletion中抛出异常,会被Spring框架吞噬,每个HandlerInterceptor的afterCompletion方法中,拿到的异常都是null——看List-4中传入的参数。
List-4
...... if (mappedHandler != null) { mappedHandler.triggerAfterCompletion(request, response, null); }}
List-5
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex) throws Exception { HandlerInterceptor[] interceptors = 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 ex2) { logger.error("HandlerInterceptor.afterCompletion threw exception", ex2); } } }}
- 位置7/8,如果之前的步骤抛出异常,就会到步骤7/8,调用triggerAfterCompletion,并将异常作为参数传入,由List-5可以看出,每个HandlerInterceptor的afterCompletion都能接收到该异常。
图1
如图1所示,
- 如果执行preHandle的链中抛出异常,那么逆向执行afterCompletion,不会执行postHandle;如果postHandle抛出异常,则逆向执行afterCompletion链。
- 如果没有抛出异常,正常情况下,先执行完preHandle链,之后调用controller的方法,之后调用逆向postHandle链,之后逆向执行afterCompletion链,如图1中右边的竖行方向调用所示。
通过源码可知,用HandlerInterceptor拦截全局异常,有点不靠谱,要深入理解源码,不然有可能达不到预期的效果。
思考
- 我们自定义的,这些HandlerInterceptor是如何被接入到Spring中的?
- Filter、HandlerInterceptor、@ControllerAdvice的调用顺序?
- 定义多个HandlerIntercepto时,如何指定顺序?
Reference
- SpringFramework源码