1. 程式人生 > >DispatcherServlet原始碼流程分析

DispatcherServlet原始碼流程分析

DispatcherServlet是Spring MVC的核心內容,理解它的具體服務流程對於中高階應用開發是不可缺少的,下面將通過對原始碼進行註釋來分析下DispatcherServlet的流程處理

  • doService

作為分發請求的方法,doService方法的作用和servlet中的doService作用類似,接收http請求,然後提供對應的服務

	/**
	 * Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch}
	 * for the actual dispatching.
	 */
	@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) + "]");
		}

        //快照處理,使用快照可以更快響應使用者請求
		// Keep a snapshot of the request attributes in case of an include,
		// to be able to restore the original attributes after the include.
		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("org.springframework.web.servlet")) {
					attributesSnapshot.put(attrName, request.getAttribute(attrName));
				}
			}
		}

		// Make framework objects available to handlers and view objects.
        //web IOC容器設定
		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);
				}
			}
		}
	}

這個方法中,做了對快照的處理和部分屬性的設定,重要的是所跳轉到的doDispatch方法

  • doDispatch

	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
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

                //開始處理請求頭,即對GET或POST請求進行處理
				String method = request.getMethod();
				boolean isGet = "GET".equals(method);
                //GET處理
				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;
				}

				//執行處理器,返回ModelAndView
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}
                //設定檢視名稱
				applyDefaultViewName(processedRequest, mv);
                //執行攔截器後的方法
				mappedHandler.applyPostHandle(processedRequest, response, mv);
			}
			catch (Exception ex) {
				dispatchException = ex;
			}
            //對請求結果做解析,如果是邏輯檢視,則解析名稱,否則不解析,最後渲染檢視
			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
		}

        //都是對異常的處理方法
		catch (Exception ex) {
			triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
		}
		catch (Error err) {
			triggerAfterCompletionWithError(processedRequest, response, mappedHandler, 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);
				}
			}
		}
	}

Dispatch方法的主要流程是:

  1. 通過請求找到對應的處理鏈,包括攔截器和控制器
  2. 通過處理器找到對應的介面卡
  3. 執行攔截器的事前方法,如果返回false,則流程結束
  4. 通過介面卡執行處理器,返回模型和檢視
  5. 對檢視和模型進行處理,執行攔截器事後方法
  6. 進行檢視和模型的解析操作
  • getHandler

在Dispatch方法中,用到了處理器和攔截器,這也是Dispatch方法的核心,接下來就是獲得處理器的方法getHandler

	protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        //遍歷HandlerMapping
		for (HandlerMapping hm : this.handlerMappings) {
			if (logger.isTraceEnabled()) {
				logger.trace(
						"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
			}
            //將獲得的HandlerMapping物件進行轉換HandlerExecutionChain物件
			HandlerExecutionChain handler = hm.getHandler(request);
			if (handler != null) {
				return handler;
			}
		}
		return null;
	}

HandlerMapping是SpringMVC啟動時初始化的處理器對映,這裡做的操作主要是轉換成HandlerExecutionChain物件,也就是攔截器鏈,成員屬性如下

public class HandlerExecutionChain {

	private static final Log logger = LogFactory.getLog(HandlerExecutionChain.class);

    //控制器物件
	private final Object handler;
    //攔截器陣列
	private HandlerInterceptor[] interceptors;
    //攔截器連結串列
	private List<HandlerInterceptor> interceptorList;
    //攔截器下標,在對攔截器進行註冊時起定位作用
	private int interceptorIndex = -1;
......
}

在這個物件裡用到的成員屬性都是使用HandlerInterceptor宣告的,點進HandlerInterceptor,可以看到有一段對HandlerInterceptor的介紹

 * <p>HandlerInterceptor is basically similar to a Servlet Filter, but in
 * contrast to the latter it just allows custom pre-processing with the option
 * of prohibiting the execution of the handler itself, and custom post-processing.
 * Filters are more powerful, for example they allow for exchanging the request
 * and response objects that are handed down the chain. Note that a filter
 * gets configured in web.xml, a HandlerInterceptor in the application context.

翻譯過來就是:HandlerInterceptor基本上類似於Servlet過濾器,但與後者不同的是,它允許自定義預處理和自定義後處理,比過濾器更強大,例如,它們允許在鏈式處理中傳遞equest和response請求,過濾器是在web.xml中配置的,而攔截器是在application中定義的

這裡的自定義預處理和後處理也就是剛才在Dispatch方法中標註出來的操作

HandlerInterceptor有三個方法

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;

}

分別對應攔截器處理前,處理時,處理後所用到的方法,這就是處理器handler中攔截器的內容

dispatch方法的流程中最後是對檢視進行解析,原始碼裡就是processDispatchResult方法對檢視路徑進行解析,對檢視型別進行判斷,比較簡單,就不粘出來了