SpringMVC執行流程及原始碼分析

二七零零發表於2021-03-06

SpringMVC流程及原始碼分析

前言

​ 學了一遍SpringMVC以後,想著做一個總結,複習一下。複習寫下面的總結的時候才發現,其實自己學的並不徹底、牢固、也沒有學全,視訊跟書本是要結合起來一起,每一位老師的視訊可能提到的東西都不一致,也導致也不是很全面,書本上會講的筆記系統、全面。同時我自己也是一個初學者,下面總結的可能並不完善、正確,希望看到的大神給我指出,在此非常感謝。


一 、Spring核心模組

1、核心模組

​ Spring Web MVC (下文簡稱為 SpringMVC )是 Spring 提供 Web 應用的框架設計,屬於表現層的框架。SpringMVC是Spring框架的一部分。
Spring框架包括大致六大模組,核心容器(Core Container)、AOP和裝置支援、資料訪問及整合、Web、報文傳送、Test

2021022601-01-Spring5核心模組圖解

圖片來源於Spring官網5.0.0.M5:

https://docs.spring.io/spring-framework/docs/5.0.0.M5/spring-framework-reference/html/overview.html#overview-modules

對於Spring5模組圖,有2點疑問:
1、不清楚為什麼在Spring官網上5.0版本以後,Release版(穩定版)的都未找到模組圖,但是在M(里程碑版)版找到 了,如果有人在5.0以後的Release版(穩定版)找到,麻煩給我留個言,謝謝。
2、在其他博文中看到Spring5模組結構圖是這樣的:

2021022601-02-Spring5核心模組圖解-疑問

挺奇怪這個圖是哪裡來的?(路過的大神請指點)

對於問題2,我在Spring5.2.13.RELEASE GA中,找到了如下所示資訊:

2021022601-03-Spring5核心模組圖解-5.2.13.RELEASE

拷貝以上資訊:

Spring Framework Documentation

Version 5.2.13.RELEASE

What’s New, Upgrade Notes, Supported Versions, and other topics, independent of release cadence, are maintained externally on the project’s Github Wiki.

Overview history, design philosophy, feedback, getting started.
Core IoC Container, Events, Resources, i18n, Validation, Data Binding, Type Conversion, SpEL, AOP.
Testing Mock Objects, TestContext Framework, Spring MVC Test, WebTestClient.
Data Access Transactions, DAO Support, JDBC, O/R Mapping, XML Marshalling.
Web Servlet Spring MVC, WebSocket, SockJS, STOMP Messaging.
Web Reactive Spring WebFlux, WebClient, WebSocket.
Integration Remoting, JMS, JCA, JMX, Email, Tasks, Scheduling, Caching.
Languages Kotlin, Groovy, Dynamic Languages.

按照以上資訊的Web Servlet、Web Reactive已經是分屬於不同的模組了。

  • Web Servlet:Spring MVC, WebSocket, SockJS, STOMP Messaging.
  • Web Reactive:Spring WebFlux, WebClient, WebSocket.

Spring官方文件:https://spring.io/projects/spring-framework#learn/

2、Spring版本命名規則(補充)

上面提到了Spring又不同的版本,在此記錄一下各個版本的意義。

描述方式 說明 含義
Snapshot 快照版 尚不穩定,仍處於開發中的版本
Release 穩定版 功能相對穩定,可以對外發行,但有時間限制
GA 正式版 代表廣泛可用的穩定版(General Availability)
M 里程碑版 (M是Milestone的意思)具有一些全新的功能或是有意義的版本
RC 終測版 Release Candidate(最終測試),即將作為正式版釋出

二、SpringMVC流程及原理

1、執行流程

SpringMVC執行流程圖
2021022601-04-魯班學院-springmvc流程圖-2清晰點圖片來源:三、引用參考資料

1.1、執行流程

  • 01、使用者傳送出請求到前端控制器(中央處理器)DispatcherServlet進行處理。

  • 02、前端控制器DispatcherServlet收到請求後,呼叫處理器對映器HandlerMapping。

  • 03、處理器對映器HandlerMapping(處理器對映器)根據request請求的URL等資訊查詢能夠進行處理的Handler,以及相關攔截器interceptor,並構造HandlerExecutionChain執行鏈,然後將構造好的HandlerExecutionChain執行鏈物件返回給前端控制器DispatcherServlet。

  • 04、前端控制器DispatcherServlet根據處理器對映器HandlerMapping的

  • 05、處理器介面卡HandlerAdapter經過適配呼叫具體的處理器(Handler/Controller),即業務中自己寫的Controller。

  • 06、Controller處理完後返回ModelAndView(springmvc的封裝物件,將model和view封裝在一起)給處理器介面卡HandlerAdapter;

  • 07、處理器介面卡HandlerAdapter將Controller執行結果ModelAndView返回給前端控制器DispatcherServlet。

  • 08、前端控制器DispatcherServlet呼叫檢視解析器ViewReslover處理ModelAndView。

  • 09、檢視解析器ViewReslover解析後根據邏輯檢視名解析成物理檢視名即具體的頁面地址,生成並返回具體物件View(springmvc封裝物件,是一個介面)。

  • 10、前端控制器DispatcherServlet根據物件View進行檢視渲染,填充Model。

  • 11、前端控制器DispatcherServlet向使用者返回響應

1.2、執行流程說明:

1.2.1、第02、03說明

(1) 處理器對映器:springmvc框架中的一種物件,框架把實現了HandlerMapping介面的類都叫做對映器(多個);

(2) 處理器對映器作用:根據請求,從springmvc容器物件中獲取處理器物件(MyController controller = ctx.getBean("some")

(3) 框架把找到的處理器物件放到一個叫做處理器執行鏈(HandlerExecutionChain)的類儲存

(4) HandlerExecutionchain:類中儲存著
 a:處理器物件(MyController);
 b:專案中的所有的攔截器List

(5) 方法呼叫:HandlerExecutionChain mappedHandler - getHandler (processedRequest);

1.2.2、第04說明

(1) HandlerExecutionChain執行鏈找到對應的處理器對映器HandlerAdapter。
(2) 處理器介面卡:springmvc框架中的物件,需要實現HandlerAdapter介面,
(3) 處理器介面卡作用:執行處理器方法(呼叫MyController.doSome()得到返回值ModelAndView )
(4) 前端控制器中呼叫介面卡:HandlerAdapter ha =getHandlerAdapter (mappedHandler.getHandler());
(5) 執行處理器方法:mv= ha.handle (processedRequest, response, mappedHandler.getHandler());

第08說明:
(1) 檢視解析器:springmvc中的物件,需要實現ViewResoler介面(可以有多個)
(2) 檢視解析器作用:組成檢視完整路徑,使用字首,字尾。並建立View物件。
(3) view是一個介面,表示檢視的,在框架中jsp,htm1不是string表示,而是使用view和他的實現類表示檢視。

InternalResourceview:檢視類,表示jsp檔案,檢視解析器會建立InternalResourceView類物件。 這個物件的裡面,有一個屬性url-/WEB-INF/view/show.jsp

1.2.2、SpringMVC元件說明

  • (1). 前端控制器(DispatcherServlet):接收請求,響應結果,相當於電腦的CPU。
  • (2). 處理器對映器(HandlerMapping):根據URL去查詢處理器.
  • (3). 處理器(Handler):(需要程式設計師去寫程式碼處理邏輯的).
  • (4). 處理器介面卡(HandlerAdapter):會把處理器包裝成介面卡,這樣就可以支援多種型別的處理器,類比筆記本的介面卡(介面卡模式的應用).
  • (5). 檢視解析器(ViewResovler):進行檢視解析,多返回的字串,進行處理,可以解析成對應的頁面.

1.2.3、SpringMVC詳細流程圖

綜上所述,總結下SpringMVC的詳細流程圖:

2021022601-05-springmvc流程圖-詳細
圖片來源:三、引用參考資料

二、原始碼分析

以下原始碼來源jar包:spring-webmvc-5.25.RELEASE.jar

1、初始化

1.1、ApplicationContext

​ ApplicationContext初始化入口類:ApplicationObjectSupport的setApplicationContext方法,setApplicationContext方法中核心部分就是初始化容器initApplicationContext(context),子類AbstractDetectingUrlHandlerMapping實現了該方法。
類圖:
2021022601-06-RequestMappingHandlerMapping類層次圖
UML圖:
2021022601-07-RequestMappingHandlerMapping-uml
​ RequestMappingHandlerMapping ,用於註解@Controller,@RequestMapping來定義controller.
初始化時,3個類的大致分工如下:

  • AbstractHandlerMethodMapping定義整個演算法流程;
  • RequestMappingInfoHandlerMapping提供匹配條件RequestMappingInfo的解析處理;
  • RequestMappingHandlerMapping根據@RequestMapping註解生成 RequestMappingInfo,同時提供isHandler實現

2、前端控制器(中央處理器)DistepcherServlet

​ 從上面的流程圖可以看到前端控制器(中央處理器)DistepcherServlet是SpringMVC核心,檢視DistepcherServlet類的繼承情況。
UML圖:
![2021022601-08-DispatcherServlet UML圖](https://gitee.com/chuchq/blogs-gallery/raw/master/images / 2021/2021022601-08-DispatcherServlet UML圖.png)
從繼承關係看出:
​ DistepcherServlet ---> FrameworkServlet ---> HttpServletBean---> HttpServlet
​ 那就說明DistepcherServlet 類也是一個Servlet類,那最終核心的方法就是service()方法,即Servlet的核心方法。
​ 那就找service()方法,在DistepcherServlet中沒有servic()方法,在父類FrameworkServlet有service()方法,原始碼如下:
來源:

org.springframework.web.servlet.FrameworkServlet.service(HttpServletRequest request, HttpServletResponse response)

/**
	 * Override the parent class implementation in order to intercept PATCH requests.
	 */
	@Override
	protected void service(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
		if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
			processRequest(request, response);
		}
		else {
			super.service(request, response);
		}
	}

可以看到:
FrameworkServlet.service(HttpServletRequest request, HttpServletResponse response)拿到request請求,判斷當前請求是否是PATCH請求,不是的就呼叫父類的servic()方法,掉用父類中的service方法就是去呼叫該類中doPost(),doGet()方法,根據不同的請求方式然後走doPost()或者doGet(),呼叫中以doGet()為例,
FrameworkServlet類的doGet()原始碼:

/**
	 * Delegate GET requests to processRequest/doService.
	 * <p>Will also be invoked by HttpServlet's default implementation of {@code doHead},
	 * with a {@code NoBodyResponse} that just captures the content length.
	 * @see #doService
	 * @see #doHead
	 */
	@Override
	protected final void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		processRequest(request, response);
	}

​ doGet()又呼叫FrameworkServlet類中的processRequest(request, response);

/**
	 * 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 | 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();
			}
			logResult(request, response, failureCause, asyncManager);
			publishRequestHandledEvent(request, response, startTime, failureCause);
		}
	}

​ processRequest(request, response)方法中最關鍵的又呼叫了doService(request, response);檢視FrameworkServlet類中的doService(request, response),或者是除錯跟蹤可知,doService(request, response)由子類DispatcherServlet實現。

原始碼來源:

org.springframework.web.servlet.FrameworkServlet.doService(HttpServletRequest request, HttpServletResponse response)

/**
	 * Subclasses must implement this method to do the work of request handling,
	 * receiving a centralized callback for GET, POST, PUT and DELETE.
	 * <p>The contract is essentially the same as that for the commonly overridden
	 * {@code doGet} or {@code doPost} methods of HttpServlet.
	 * <p>This class intercepts calls to ensure that exception handling and
	 * event publication takes place.
	 * @param request current HTTP request
	 * @param response current HTTP response
	 * @throws Exception in case of any kind of processing failure
	 * @see javax.servlet.http.HttpServlet#doGet
	 * @see javax.servlet.http.HttpServlet#doPost
	 */
	protected abstract void doService(HttpServletRequest request, HttpServletResponse response)
			throws Exception;

​ 檢視DispatcherServlet中的doService(HttpServletRequest request, HttpServletResponse response)方法

/**
	 * 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 {
		logRequest(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<>();
			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));
				}
			}
		}

		// 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());

		if (this.flashMapManager != null) {
			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);
				}
			}
		}
	}

​ DispatcherServlet的doService()方法中最終呼叫doDispatch(request, response),檢視原始碼如下:
org.springframework.web.servlet.DispatcherServlet.doDispatch()

/**
	 * Process the actual dispatching to the handler.
	 * <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
	 * The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
	 * to find the first that supports the handler class.
	 * <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
	 * themselves to decide which methods are acceptable.
	 * @param request current HTTP request
	 * @param response current HTTP response
	 * @throws Exception in case of any kind of processing failure
	 */
	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);
				// 取得處理當前請求的controller,這裡也稱為hanlder處理器,第一個步驟的意義就在這裡體現了.這裡並不是直接返回controller,而是返回的HandlerExecutionChain請求處理器鏈物件,該物件封裝了handler和攔截器interceptors.
				// Determine handler for the current request.
				mappedHandler = getHandler(processedRequest);
				// 如果handler為空,則返回404
				if (mappedHandler == null) {
					noHandlerFound(processedRequest, response);
					return;
				}
				//3. 獲取處理request的處理器介面卡HandlerAdapter
				// 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 (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
						return;
					}
				}
				//處理器介面卡執行之前,檢查攔截器的方法
				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}
				//處理器介面卡根據找到,執行handler,返回ModelAndView
				// 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);
			}
			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);
				}
			}
		}
	}

​ 可以看出doDispatch()就是SpringMVC的核心程式碼了,分析doDispatch():

2.1、查詢處理器對映器HandlerMapping

​ 首先看下處理器對映器HandlerMapping類圖:
2021022601-09-HandlerMapping類圖

doDispatch()關鍵程式碼:

HandlerExecutionChain mappedHandler = null;

mappedHandler = getHandler(processedRequest);

​ mappedHandler是一個執行鏈HandlerExecutionChain 物件,這裡封裝了handler和攔截器interceptors,getHandler(processedRequest)方法就是從處理器對映器HandlerMapping中找到url和controller的對應關係,並返回給前端控制器DispatchServlet。
檢視getHandler(processedRequest);原始碼:

/**
	 * 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
	 */
	@Nullable
	protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		if (this.handlerMappings != null) {
			for (HandlerMapping mapping : this.handlerMappings) {
				HandlerExecutionChain handler = mapping.getHandler(request);
				if (handler != null) {
					return handler;
				}
			}
		}
		return null;
	}

除錯程式碼如下:
2021022601-10-DispatchServlet-doDispatch-getHandler執行截圖
從程式碼除錯中可以看到handlerMapping中有三個物件:

this.handlerMappings = {ArrayList@4662}  size = 3
 0 = {BeanNameUrlHandlerMapping@4791} 
 1 = {RequestMappingHandlerMapping@4792} 
 2 = {RouterFunctionMapping@4793} 
  • BeanNameUrlHandlerMapping:初始化時會將urlpath做對映儲存(xml);

  • RequestMappingHandlerMapping:初始化時會將Controller中配置@RequestMapping註解的方法做對映儲存(註解);

  • RouterFunctionMapping:
    (這個物件不是太理解)
    這也就是為什麼要去HandlerMapping找一個Handler了,因為處理器對映器HandlerMapping有不同的實現:

  • 1、xml方式

  • 2、註解方式

接著看getHandler(HttpServletRequest request)方法,先遍歷HandlerMappers,查詢控制器找到之後就返回執行鏈HandlerExecutionChain型別的Handler。

2021022601-11-DispatchServlet-doDispatch-getHandler-執行截圖

可以看到返回的Handler中,拿到的就是我們自己編碼的Controller類,以及攔截器(演示專案中未編寫,所以除錯彙總返回的Handler最後是0 interceptors)
HandlerExecutionChain with [com.bjpowernode.controller.MyController#doSome()] and 0 interceptors
2021022601-12-02-DispatchServlet-doDispatch-getHandler-mappedHandler執行截圖2
將正在除錯的idea開啟自己編寫的Controller來對照,發現一致:
2021022601-13-DispatchServlet-doDispatch-getHandler-執行返回結果截圖

2.2、根據處理器對映器HandlerMapping返回結果呼叫處理器介面卡HandlerAdapter

doDispatch()裡面的關鍵程式碼:

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

原始碼如下:

/**
	 * 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 {
		if (this.handlerAdapters != null) {
			for (HandlerAdapter adapter : this.handlerAdapters) {
				if (adapter.supports(handler)) {
					return adapter;
				}
			}
		}
		throw new ServletException("No adapter for handler [" + handler +
				"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
	}

為什麼還要獲取處理器介面卡HandlerAdapter:與獲取處理器對映器HandlerMapping一樣,Spring提供了不通的處理器介面卡。
除錯如下:

2021022601-14-01-DispatchServlet-doDispatch-getHandlerAdapter-執行截圖-出方法-在用
檢視DEBUG除錯模式中getHandlerAdapter()方法在中的:
handler、adapter、this.handlerAdapters

2021022601-15-DispatchServlet-doDispatch-getHandlerAdapter-執行結果截圖
以下是拷貝的結果:
handler

handler = {HandlerMethod@4792} "com.bjpowernode.controller.MyController#doSome()"
 logger = {LogAdapter$JavaUtilLog@4858} 
 bean = {MyController@4859} 
 beanFactory = {DefaultListableBeanFactory@4847} "org.springframework.beans.factory.support.DefaultListableBeanFactory@56b5a4c3: defining beans [myController,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.event.internalEventListenerProcessor,org.springframework.context.event.internalEventListenerFactory,org.springframework.web.servlet.view.InternalResourceViewResolver#0]; root of factory hierarchy"
 beanType = {Class@3782} "class com.bjpowernode.controller.MyController"
 method = {Method@4860} "public org.springframework.web.servlet.ModelAndView com.bjpowernode.controller.MyController.doSome()"
 bridgedMethod = {Method@4860} "public org.springframework.web.servlet.ModelAndView com.bjpowernode.controller.MyController.doSome()"
 parameters = {MethodParameter[0]@4861} 
 responseStatus = null
 responseStatusReason = null
 resolvedFromHandlerMethod = {HandlerMethod@4863} "com.bjpowernode.controller.MyController#doSome()"
 interfaceParameterAnnotations = null
 description = "com.bjpowernode.controller.MyController#doSome()"

adapter

adapter = {RequestMappingHandlerAdapter@4827} 
 customArgumentResolvers = null
 argumentResolvers = {HandlerMethodArgumentResolverComposite@4833} 
 initBinderArgumentResolvers = {HandlerMethodArgumentResolverComposite@4834} 
 customReturnValueHandlers = null
 returnValueHandlers = {HandlerMethodReturnValueHandlerComposite@4835} 
 modelAndViewResolvers = null
 contentNegotiationManager = {ContentNegotiationManager@4836} 
 messageConverters = {ArrayList@4837}  size = 4
 requestResponseBodyAdvice = {ArrayList@4838}  size = 0
 webBindingInitializer = null
 taskExecutor = {SimpleAsyncTaskExecutor@4839} 
 asyncRequestTimeout = null
 callableInterceptors = {CallableProcessingInterceptor[0]@4840} 
 deferredResultInterceptors = {DeferredResultProcessingInterceptor[0]@4842} 
 reactiveAdapterRegistry = {ReactiveAdapterRegistry@4844} 
 ignoreDefaultModelOnRedirect = false
 cacheSecondsForSessionAttributeHandlers = 0
 synchronizeOnSession = false
 sessionAttributeStore = {DefaultSessionAttributeStore@4845} 
 parameterNameDiscoverer = {DefaultParameterNameDiscoverer@4846} 
 beanFactory = {DefaultListableBeanFactory@4847} "org.springframework.beans.factory.support.DefaultListableBeanFactory@56b5a4c3: defining beans [myController,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.event.internalEventListenerProcessor,org.springframework.context.event.internalEventListenerFactory,org.springframework.web.servlet.view.InternalResourceViewResolver#0]; root of factory hierarchy"
 sessionAttributesHandlerCache = {ConcurrentHashMap@4848}  size = 0
 initBinderCache = {ConcurrentHashMap@4849}  size = 0
 initBinderAdviceCache = {LinkedHashMap@4850}  size = 0
 modelAttributeCache = {ConcurrentHashMap@4851}  size = 0
 modelAttributeAdviceCache = {LinkedHashMap@4852}  size = 0
 order = 2147483647
 supportedMethods = null
 allowHeader = "GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS"
 requireSession = false
 cacheControl = null
 cacheSeconds = -1
 varyByRequestHeaders = null
 useExpiresHeader = false
 useCacheControlHeader = true
 useCacheControlNoStore = true
 alwaysMustRevalidate = false
 servletContext = {ApplicationContextFacade@4754} 
 logger = {LogAdapter$JavaUtilLog@4854} 
 applicationContext = {XmlWebApplicationContext@4665} "WebApplicationContext for namespace 'myweb-servlet', started on Tue Mar 02 23:25:35 CST 2021"
 messageSourceAccessor = {MessageSourceAccessor@4855} 

this.handlerAdapters

this.handlerAdapters = {ArrayList@4658}  size = 4
 0 = {HttpRequestHandlerAdapter@4810} 
 1 = {SimpleControllerHandlerAdapter@4820} //XML方式
 2 = {RequestMappingHandlerAdapter@4827} //註解方式
 3 = {HandlerFunctionAdapter@4832} 

可以看到找到4個處理器介面卡。通過DEBUG模式可以看到,此次取到的處理器介面卡HandlerAdapter是:RequestMappingHandlerAdapter

ha = {RequestMappingHandlerAdapter@4827} 

2.3、檢查攔截器Interceptor

doDispatch()中的關鍵程式碼:

if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}
org.springframework.web.servlet.HandlerExecutionChain#applyPreHandle

applyPreHandle(processedRequest, response)原始碼:

/**
	 * Apply preHandle methods of registered interceptors.
	 * @return {@code true} if the execution chain should proceed with the
	 * next interceptor or the handler itself. Else, DispatcherServlet assumes
	 * that this interceptor has already dealt with the response itself.
	 */
	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;
	}

2.3、處理器介面卡HandlerAdapter執行Handler(Controller)返回ModelAndView

doDispatch()中的關鍵程式碼:

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

DEBUG模式除錯,是跳到了:
org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter#handle
原始碼如下:

/**
	 * This implementation expects the handler to be an {@link HandlerMethod}.
	 */
	@Override
	@Nullable
	public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {

		return handleInternal(request, response, (HandlerMethod) handler);
	}

再往下看handleInternal(request, response, (HandlerMethod) handler)方法,
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#handleInternal

@Override
	protected ModelAndView handleInternal(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

		ModelAndView mav;
		checkRequest(request);

		// Execute invokeHandlerMethod in synchronized block if required.
		if (this.synchronizeOnSession) {
			HttpSession session = request.getSession(false);
			if (session != null) {
				Object mutex = WebUtils.getSessionMutex(session);
				synchronized (mutex) {
					mav = invokeHandlerMethod(request, response, handlerMethod);
				}
			}
			else {
				// No HttpSession available -> no mutex necessary
				mav = invokeHandlerMethod(request, response, handlerMethod);
			}
		}
		else {
			// No synchronization on session demanded at all...
			mav = invokeHandlerMethod(request, response, handlerMethod);
		}

		if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
			if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
				applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
			}
			else {
				prepareResponse(response);
			}
		}

		return mav;
	}

注意,handleInternal(request, response, (HandlerMethod) handler)方法的返回值是ModelAndView ,這裡就完成了處理器介面卡HandlerAdapter執行Handler(Controller)並將結果ModelAndView返回給前端控制器DistepchServlet

2.4、檢視解析器ViewResolver

  接上2.3:前端控制器DistepchServlet接收到處理器介面卡HandlerAdapter返回的ModelAndView以後,這裡分2種情況:

  • (1)、如果ModelAndView裡面是邏輯檢視
    前端控制器DistepchServlet呼叫檢視解析器ViewResolver通過邏輯檢視查詢真正的檢視物件View,並返回給前端控制器DistepchServlet。
  • (2)、如果ModelAndView裡面是非邏輯檢視:
    如:MappingJackson2JsonView(把當前資料轉為為JSON資料,並不需要對檢視邏輯名稱進行轉換)

總結一下:
檢視解析器ViewResolver介面主要作用是解析前端控制器DispatcherServlet傳遞的邏輯檢視名,並將解析結果的真正的檢視物件View傳回給前端控制器DispatcherServlet

ViewResolverd的實現類:

2021022601-16-ViewResolver-實現類圖-hierarchy
ViewResolver的UML:

2021022601-17-ViewResolver-uml圖

2.5、檢視View

2.5.1、檢視物件的作用

  • (1)、將控制器返回的資料處理渲染,最終返回客戶端展示給使用者,主要就是完成轉發或者是重定向的操作.。
  • (2)、為了實現檢視模型和具體實現技術的解耦(指的是Spring在org.springframework.web.servlet包中定義的抽象View介面),詳見2.5.2View介面圖。
  • (3)、檢視物件View由檢視解析器負責例項化。由於檢視是無狀態(每一次請求都會建立一個新的view物件)的,所以不會有執行緒安全的問題.

2.5.2、View介面圖

2021022601-18-View-介面方法

2.5.3、View的實現類圖

2021022601-19-View-實現類圖-hierarchy

2.5.4、View的UML圖

![2021022601-20-01-View-uml(hierarchic group layout)](https://gitee.com/chuchq/blogs-gallery/raw/master/images / 2021/2021022601-20-01-View-uml(hierarchic group layout).png)

2.5.5、常用的View檢視類

檢視型別 簡介
URL檢視資源圖 InternalResourceView 將JSP或其他資源封裝成一個檢視。被檢視解析器InternalResourceViewResolver預設使用。
JstlView InternalResourceView的子類。如果JSP中使用了JSTL的國際化標籤,就需要使用該檢視類。
文件檢視 AbstractExcelView Excel文件檢視的抽象類。
AbstractPdfView PDF文件檢視的抽象類
報表檢視 ConfigurableJasperReportsView 常用的JasperReports報表檢視
JasperReportsHtmlView
JasperReportsPdfView
JasperReportsXlsView
JSON檢視 MappingJackson2JsonView 將資料通過Jackson框架的ObjectMapper物件,以JSON方式輸出

2.6、其他重要的點

2.6.1、DispatcherServlet.properties

DispatcherServlet.properties檔案是在SpringMVC架包中:
2021022601-21-jar-DispatchServlet.properties
DispatcherServlet.properties內容:

# Default implementation classes for DispatcherServlet's strategy interfaces.
# Used as fallback when no matching beans are found in the DispatcherServlet context.
# Not meant to be customized by application developers.

org.springframework.web.servlet.LocaleResolver=
	org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver

org.springframework.web.servlet.ThemeResolver=
	org.springframework.web.servlet.theme.FixedThemeResolver

org.springframework.web.servlet.HandlerMapping=
	org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\
	org.springframework.web.servlet.function.support.RouterFunctionMapping

org.springframework.web.servlet.HandlerAdapter=
	org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
	org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\
	org.springframework.web.servlet.function.support.HandlerFunctionAdapter


org.springframework.web.servlet.HandlerExceptionResolver=
	org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
	org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
	org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver

org.springframework.web.servlet.RequestToViewNameTranslator=
	org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator

org.springframework.web.servlet.ViewResolver=
	org.springframework.web.servlet.view.InternalResourceViewResolver

org.springframework.web.servlet.FlashMapManager=
	org.springframework.web.servlet.support.SessionFlashMapManager

SpringMVC為什麼能載入不同處理器對映器HandlerMapping、處理器介面卡handlerAdapter,就是因為框架配置了這個DispatcherServlet.properties檔案。

三、引用參考資料

1、引用資料

2、參考資料


免責宣告:
本文中使用的部分圖片來自於網路(地址見:三、引用參考資料),如有侵權,請聯絡博主進行刪除。

轉載宣告:

寫部落格不易,請尊重原作者!!

歡迎大家閱讀、轉載,如果是整文轉載請在文章開頭或者結尾處 註明 原文地址、作者,如果是大段參考備註 參考連結

相關文章