1. 程式人生 > >SpringMVC攔截器原始碼解析

SpringMVC攔截器原始碼解析

 前言:提到攔截器,我們一定會想到Spring Aop。在看SpringMVC原始碼以前,一直以為SpringMVC的攔截器是用Spring Aop的動態代理來實現,並且也很困惑,如果用動態代理如何實現攔截器?這裡就不介紹Spring Aop實現攔截器的方式,剖析SpringMVC 攔截器實現的細節。

    SpringMVC攔截器也是Aop(面向切面)思想構建,但不是SpringAop動態代理實現的,主要採用責任鏈和介面卡的設計模式來實現,直接嵌入到SpringMVC入口程式碼裡面。

    涉及到設計模式,有利於對程式碼的理解

一、攔截器的介面類結構

攔截器的頂級介面為HandleInterceptor,提供三個介面,分別是preHandle(請求前)、postHandle(請求提交)、afterCompletion(請求完成後攔截)

package org.springframework.web.servlet;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.method.HandlerMethod;
/**
*  攔截器頂級介面
*/
public interface HandlerInterceptor {

    //請求前攔截
	boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception
; //提交請求攔截 void postHandle( HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception; //請求完成後攔截 void afterCompletion( HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception
; }

而我們通常使用的是HandlerInterceptorAdapter(攔截器介面卡),運用介面介面卡模式。在實際運用中,介面中可以定義了很多方法,子類可能實現幾個方法,介面介面卡模式就起到了作用。

package org.springframework.web.servlet.handler;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.AsyncHandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

public abstract class HandlerInterceptorAdapter implements AsyncHandlerInterceptor {
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		return true;
	}
	@Override
	public void postHandle(
			HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
			throws Exception {
	}
	@Override
	public void afterCompletion(
			HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
	}
	/**
	 * This implementation is empty.
     * 來自實現AsyncHandlerInterceptor 的方法
	 */
	@Override
	public void afterConcurrentHandlingStarted(
			HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
	}

}

二、攔截器原始碼執行的呼叫流程

    1. HandlerMapping初始化

瞭解到SpringMVC攔截器介面HandlerInterceptorAdapter,我們再進入SpringMVC請求入口org.springframework.web.servlet.DispatcherServlet,攔截器運作工作流程由此展開。DispatcherServlet作為控制中心,攔截器就嵌入在入口方法中doDispatch中。SpringMVC又是如何將定義的攔截器加入進去呢?


	/**
	 * 完成Servlet一系列初始化工作,包括檢視解析器、處理器對映器、處理器介面卡、主題解析器...
	 */
	protected void initStrategies(ApplicationContext context) {
		initMultipartResolver(context);
		initLocaleResolver(context);
		initThemeResolver(context);
        // 初始化RequestMapping,攔截器是作用在RequestMapping
		initHandlerMappings(context);
		initHandlerAdapters(context);
		initHandlerExceptionResolvers(context);
		initRequestToViewNameTranslator(context);
		initViewResolvers(context);
		initFlashMapManager(context);
	}

初始化RequestMapping的時候,獲取到RequestMapping的集合,因為攔截器作用在單個RequestMapping,來實現攔截器鏈。這裡就不詳細介紹RequestMapping的獲取。

    2. DispatcherServlet執行呼叫,doService作為Servlet主要執行者,通過呼叫doDispatch來真正執行請求處理

在initStrategies(ApplicationContext context)完成初始化準備工作後,來正式進入DispatcherServlet請求進入的處理過程。DispatcherServlet基於Servlet開發,它的執行方式是 doService(HttpServletRequest request, HttpServletResponse response) 

   /**
    *  doService做了一部分請求準備預處理的工作, 而實際處理的工作呼叫doDispatch(request, response)
    */
	@Override
	protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
		if (logger.isDebugEnabled()) {
			String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
			logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
					" processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
		}

		// 對請求屬性快照的保持和儲存後的使用,
		Map<String, Object> attributesSnapshot = null;
		if (WebUtils.isIncludeRequest(request)) {
			attributesSnapshot = new HashMap<String, Object>();
			Enumeration<?> attrNames = request.getAttributeNames();
			while (attrNames.hasMoreElements()) {
				String attrName = (String) attrNames.nextElement();
				if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
					attributesSnapshot.put(attrName, request.getAttribute(attrName));
				}
			}
		}

		//對SpringMVC框架中處理器Handle和檢視渲染,提前做準備工作
		request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
		request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
		request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
		request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
        // FlashMap是對重定向的引數傳遞的處理
		FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
		if (inputFlashMap != null) {
			request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
		}
		request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
		request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);

		try {
           //***請求的實際處理邏輯***-----進入方法
			doDispatch(request, response);
		}
		finally {
			if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
				// Restore the original attribute snapshot, in case of an include.
				if (attributesSnapshot != null) {
					restoreAttributesAfterInclude(request, attributesSnapshot);
				}
			}
		}
	}

    3. doDispatch中完成攔截器的新增和攔截器攔截處理

 doService做了一部分請求準備預處理的工作,包括請求中設定處理器和檢視的屬性,作用於後續處理器的運作和檢視渲染, 而實際請求邏輯處理的工作呼叫doDispatch(request, response),也是我們需要重點跟進的程式碼。或許大家初次看doDispatch方法中的程式碼,還不知道攔截器在哪裡運作。

處理流程是:

  1.     通過getHandler(HttpServletRequest request)獲取到HandlerExecutionChain處理器執行鏈,將攔截器注入到HandlerExecutionChain屬性
  2.   分別呼叫HandlerExecutionChain的三個方法,applyPreHandle、applyPostHandle、triggerAfterCompletion,實現前置攔截/請求提交攔截和請求完成後攔截。使用責任鏈的設計模式,實際呼叫的是HandleInterceptor的三個介面,分別對應preHandle、postHandle、afterCompletion
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);
				//***獲取到請求執行鏈,攔截器的新增在裡面完成***---進入方法
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null || mappedHandler.getHandler() == null) {
					noHandlerFound(processedRequest, response);
					return;
				}
				// 獲取到相應處理器介面卡
				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;
					}
				}
                //1.請求前攔截處理---進入方法細看(責任鏈模式,呼叫攔截器鏈的執行)
				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}
				// Actually invoke the handler.
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

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

				applyDefaultViewName(processedRequest, mv);
                //2.請求提交的攔截處理---進入方法細看(責任鏈模式,呼叫攔截器鏈的執行)
				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);
			}
            // 3.請求結果檢視渲染完成後,呼叫mappedHandler.triggerAfterCompletion(),實現請求完成後攔截
			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
		}
		catch (Exception ex) {
			triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
		}
		catch (Throwable err) {
			triggerAfterCompletion(processedRequest, response, mappedHandler,
					new NestedServletException("Handler processing failed", err));
		}
		finally {
			if (asyncManager.isConcurrentHandlingStarted()) {
				// Instead of postHandle and afterCompletion
				if (mappedHandler != null) {
					mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
				}
			}
			else {
				// Clean up any resources used by a multipart request.
				if (multipartRequestParsed) {
					cleanupMultipart(processedRequest);
				}
			}
		}
	}

三、結合責任鏈設計在攔截器中的運用

    責任鏈模式的定義:使多個物件都有機會處理請求,從而避免請求的傳送者和接受者之間的耦合關係, 將這個物件連成一條鏈,並沿著這條鏈傳遞該請求,直到有一個物件處理他為止。

分析 1、如何將攔截器新增到HandlerExecutionChain中

    首先遍歷HandlerMappings集合,HandlerMappings是在initHandlerMappings(context)初始化得到。根據request查詢到相應 HandlerMapping的HandlerExecutionChain。

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        //遍歷處理器對映器集合,找到相應請求的處理器對映器
		for (HandlerMapping hm : this.handlerMappings) {
			if (logger.isTraceEnabled()) {
				logger.trace(
						"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
			}
            //***請求匹配到對映器,並返回HandlerExecutionChain***---進入方法細看
            //----攔截器的新增在方法裡面完成
			HandlerExecutionChain handler = hm.getHandler(request);
			if (handler != null) {
				return handler;
			}
		}
		return null;
	}
@Override
	public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		Object handler = getHandlerInternal(request);
		if (handler == null) {
			handler = getDefaultHandler();
		}
		if (handler == null) {
			return null;
		}
		// Bean name or resolved handler?
		if (handler instanceof String) {
			String handlerName = (String) handler;
			handler = getApplicationContext().getBean(handlerName);
		}
        //***獲取HandlerExecutionChain,攔截器的新增 ***---進一步跟進程式碼
		HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
		if (CorsUtils.isCorsRequest(request)) {
			CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
			CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
			CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
			executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
		}
		return executionChain;
	}
/**
 * 走到這裡,才真正看到了HandlerExecutionChain的建立和攔截器的新增
 */
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
        //獲取到HandlerExecutionChain 
		HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
				(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));

		String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
        //遍歷攔截器,adaptedInterceptors也是在initHandlerMapping初始化得到
		for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
			if (interceptor instanceof MappedInterceptor) {
				MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
				if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
                    //將攔截器新增
					chain.addInterceptor(mappedInterceptor.getInterceptor());
				}
			}
			else {
				chain.addInterceptor(interceptor);
			}
		}
		return chain;
	}

分析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;
	}
/**
	 * Apply postHandle methods of registered interceptors.
	 */
	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);
			}
		}
	}
/**
	 * Trigger afterCompletion callbacks on the mapped HandlerInterceptors.
	 * Will just invoke afterCompletion for all interceptors whose preHandle invocation
	 * has successfully completed and returned true.
	 */
	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);
				}
			}
		}
	}