8.☆SpringMVC源碼剖析其運行過程☆

探索SpringMVC的祕密

我們都知道在Springmvc中前端控制器,是執行的核心控制器,從繼承關係上看其實DispatcherServlet就是一個servlet。這時候我們回顧一下DispatcherServlet是怎麼配置的?是不是在<servlet></servlet>裏面配置的,我們剛學習servlet的時候是不是自定義servlet也是在web.xml中通過<servlet></servlet>配置的呢?答案都是yes,下面的圖瞭解一下,就繼續看下面的內容…

在這裏插入圖片描述

我們學過javaweb部分都知道servlet的生命週期中有一個service方法是提供服務的方法,每次訪問servlet的時候,service方法就會被調用一次。

在FrameworkServlet中有service方法,源碼如下:

重點關注:

  • processRequest(request, response);

  • super.service(request, response);

	/**
	 * Override the parent class implementation in order to intercept PATCH
	 * requests.
	 * 重寫父類實現以攔截PATCH請求。
	 */
	@Override
	protected void service(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		String method = request.getMethod();
		if (method.equalsIgnoreCase(RequestMethod.PATCH.name())) {
			processRequest(request, response);
		}
		else {
            // 
			super.service(request, response);
		}
	}

這裏做了 一下請求方式的判斷如果請求的方式是下面這幾種的話調用processRequest()方法,否則調用父類的service方法,即HttpServlet的service方法。

public enum RequestMethod {
	GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE
}

這裏我們進入processRequest()方法,源碼如下:

處理此請求,發佈事件,無論結果如何。

實際的事件處理由抽象的doService模板方法執行,所以我們只需要關注其中調用的doService()方法,其他代碼一笑而過...

/**
	 * Process this request, publishing an event regardless of the outcome.
	 * <p>The actual event handling is performed by the abstract
	 * {@link #doService} template method.
	 */
	protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		long startTime = System.currentTimeMillis();
		Throwable failureCause = null;

		LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
		LocaleContext localeContext = buildLocaleContext(request);

		RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
		ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
		asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());

		initContextHolders(request, localeContext, requestAttributes);

		try {
			doService(request, response);
		}
		catch (ServletException ex) {
			failureCause = ex;
			throw ex;
		}
		catch (IOException ex) {
			failureCause = ex;
			throw ex;
		}
		catch (Throwable ex) {
			failureCause = ex;
			throw new NestedServletException("Request processing failed", ex);
		}

		finally {
			resetContextHolders(request, previousLocaleContext, previousAttributes);
			if (requestAttributes != null) {
				requestAttributes.requestCompleted();
			}

			if (logger.isDebugEnabled()) {
				if (failureCause != null) {
					this.logger.debug("Could not complete request", failureCause);
				}
				else {
					if (asyncManager.isConcurrentHandlingStarted()) {
						logger.debug("Leaving response open for concurrent processing");
					}
					else {
						this.logger.debug("Successfully completed request");
					}
				}
			}

			publishRequestHandledEvent(request, startTime, failureCause);
		}
	}

點擊doService()方法之後,我們發現它在 FrameworkServlet中是一個抽象的方法,所以它的子類一定會實現它,我們回到DispatcherServlet中尋找doService()方法,其源代碼如下:

公開特定於DispatcherServlet的請求屬性,並將其委託給doDispatch()進行實際的調度,同上我們只需要找到doDispatch()方法,其他代碼一笑而過....

	/**
	 * 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 requestUri = urlPathHelper.getRequestUri(request);
			String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
			logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
					" processing " + request.getMethod() + " request for [" + requestUri + "]");
		}

		// 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)) {
			logger.debug("Taking snapshot of request attributes before include");
			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.
		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 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()) {
				return;
			}
			// Restore the original attribute snapshot, in case of an include.
			if (attributesSnapshot != null) {
				restoreAttributesAfterInclude(request, attributesSnapshot);
			}
		}
	}

doDispatch()方法

當我們進入doDispatch()方法,下面好戲就開始了…

下面的內容是翻譯自doDispatch()方法的註釋:

  1. 將實際的分派處理程序處理。
  2. 該處理程序將通過依次應用servlet的處理程序映射來獲得。
  3. HandlerAdapter將通過查詢servlet已安裝的HandlerAdapter來找到第一個支持handler類的HandlerAdapter。
  4. 所有HTTP方法都由此方法處理。
  5. 由處理器適配器或處理器本身來決定哪些方法是可接受的。

特別是第3點,是促使我探究源碼的原因,因爲這裏使用了設計模式中適配器模式的思想實現的。

下面將對這個方法進行詳細的解釋:

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.
                // 確定當前請求的處理程序。
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null || mappedHandler.getHandler() == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

				// Determine handler adapter for the current request.
				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()) {
						String requestUri = urlPathHelper.getRequestUri(request);
						logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
					}
					if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
						return;
					}
				}

				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}

				try {
					// Actually invoke the handler.
                    // 實際調用處理程序。
					mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
				}
				finally {
					if (asyncManager.isConcurrentHandlingStarted()) {
						return;
					}
				}

				applyDefaultViewName(request, 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
				mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
				return;
			}
			// Clean up any resources used by a multipart request.
			if (multipartRequestParsed) {
				cleanupMultipart(processedRequest);
			}
		}
	}

確定當前請求的處理程序getHandler

mappedHandler = getHandler(processedRequest);

我們進入到DispatcherServlet的getHandler()方法,源碼如下:

  1. 返回此請求的HandlerExecutionChain即處理程序執行鏈,由處理程序對象和任何處理程序攔截器組成。
  2. 依次嘗試所有處理程序映射。
  3. 進入到此方法中就拿到this.handlerMappings,而this.handlerMappings是此Servlet使用的HandlerMapping列表。
  4. 在這裏插入圖片描述
  5. 遍歷this.handlerMappings拿到處理程序執行鏈,我測試使用的控制器是使用註解@Controller進行配置的,所以通過RequestMappingHandlerMapping就拿到了處理程序執行鏈。當然下面那個也是有用的,具體看我們的控制器是有什麼方式配置的。
	/**
	 * Return the HandlerExecutionChain for this request.
	 * <p>Tries all handler mappings in order.
	 * @param request current HTTP request
	 * @return the HandlerExecutionChain, or {@code null} if no handler could be found
	 */
	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 handler = hm.getHandler(request);
			if (handler != null) {
				return handler;
			}
		}
		return null;
	}

返回結果爲:處理程序執行鏈HandlerExecutionChain

在這裏插入圖片描述

確定當前請求的處理程序適配器getHandlerAdapter

HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

返回此處理程序對象的HandlerAdapter。根據不同的handler獲取不同的handlerAdapter。

在這裏插入圖片描述

結果返回的是:RequestMappingHandlerAdapter

/**
	 * Return the HandlerAdapter for this handler object.
	 * @param handler the handler object to find an adapter for
	 * @throws ServletException if no HandlerAdapter can be found for the handler. This is a fatal error.
	 */
	protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
		for (HandlerAdapter ha : this.handlerAdapters) {
			if (logger.isTraceEnabled()) {
				logger.trace("Testing handler adapter [" + ha + "]");
			}
			if (ha.supports(handler)) {
				return ha;
			}
		}
		throw new ServletException("No adapter for handler [" + handler +
				"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
	}

通過拿到的RequestMappingHandlerAdapter去執行handler,即去執行控制器也就是我配置的HelloWorldController

實際調用處理程序

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

到了這一步我們就得到了ModelAndView對象,然後根據ModelAndView對象去找對應的視圖解析器去解析視圖,返回邏輯視圖,然後渲染視圖返回就可以了。

小結

如果你跟着上面的思路,並熟悉配置ControllerModelAndView,視圖解析器設計模式-適配器模式的話再結合着下面這張圖,我相信你會非常通透。

在這裏插入圖片描述

附:

DispatcherServlet.properties

兩種HandlerMapping

  1. BeanNameUrlHandlerMapping
  2. DefaultAnnotationHandlerMapping

三種HandlerAdapter

  1. HttpRequestHandlerAdapter
  2. SimpleControllerHandlerAdapter
  3. AnnotationMethodHandlerAdapter
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
	org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping

org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
	org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
	org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
定義的Controller

調試的過程中斷點大多在都doDispatch()方法中。

/**
 * @Date 2020/6/16 17:22
 * @Version 10.21
 * @Author DuanChaojie
 */
@Controller
@RequestMapping("/user")
public class HelloWorldController {

    @RequestMapping(value = "/justweb" )
    public String hello(){
        System.out.println("使用註解配置的Controller,訪問成功了...\n\r開始源碼分析....");
        return "success";
    }

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章