SpringFramework之HandlerInterceptor

    最近在使用Spring時,總感受對HandlerInterceptor有點模糊,回頭再來看看,記錄下。java

    SpringFramework的版本4.3.x.RELEASE.ios

    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.
			//一、獲得handlerExecutionChain,裏面含有對應的Controller,裏面還有interceptor,
			mappedHandler = getHandler(processedRequest);
			if (mappedHandler == null || mappedHandler.getHandler() == null) {
				noHandlerFound(processedRequest, response);
				return;
			}

			// Determine handler adapter for the current request.
			//二、會根據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;
				}
			}
            //三、
			if (!mappedHandler.applyPreHandle(processedRequest, response)) {
				return;
			}

			// Actually invoke the handler.
			//四、真正進行反射操做,調用方法
			mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

			if (asyncManager.isConcurrentHandlingStarted()) {
				return;
			}

			applyDefaultViewName(processedRequest, mv);
            //五、
			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-2app

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);
		}
	}
}
  • 位置六、若是以前沒有拋出異常,就調用processDispatchResult方法,方法processDispatchResult的最後,有以下List-4,調用HandlerExecutionChain的triggerAfterCompletion,會逆序調用HandlerInterceptor的afterCompletion,由List-5能夠看出,afterCompletion中拋出異常,會被Spring框架吞噬,每一個HandlerInterceptor的afterCompletion方法中,拿到的異常都是null——看List-4中傳入的參數。

    List-4async

......
	if (mappedHandler != null) {
		mappedHandler.triggerAfterCompletion(request, response, null);
	}
}

    List-5post

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 this

    如圖1所示,spa

  1. 若是執行preHandle的鏈中拋出異常,那麼逆向執行afterCompletion,不會執行postHandle;若是postHandle拋出異常,則逆向執行afterCompletion鏈。
  2. 若是沒有拋出異常,正常狀況下,先執行完preHandle鏈,以後調用controller的方法,以後調用逆向postHandle鏈,以後逆向執行afterCompletion鏈,如圖1中右邊的豎行方向調用所示。

    經過源碼可知,用HandlerInterceptor攔截全局異常,有點不靠譜,要深刻理解源碼,否則有可能達不到預期的效果。

思考

  1. 咱們自定義的,這些HandlerInterceptor是如何被接入到Spring中的?
  2. Filter、HandlerInterceptor、@ControllerAdvice的調用順序?
  3. 定義多個HandlerIntercepto時,如何指定順序?

Reference

  1. SpringFramework源碼
相關文章
相關標籤/搜索