SpringMVC原始碼分析

鄧曉暉發表於2020-12-10

一,DispatcherServlet繼承結構

image-20201207222815180


二、SpringMvc請求處理的大致流程

2.1 Handler方法執行的時機

打斷點:

image-20201207223456035

觀察呼叫棧:
image-20201207223613812

doDispathch⽅法中的1064⾏程式碼完成handler⽅法的調⽤

2.2 頁面渲染時機(打斷點並觀察呼叫棧)

image-20201207223901485

3.3 doDispatch()方法核心步驟 (Springmvc處理請求的大致流程):

  1. 呼叫getHandler()獲取到能夠處理當前請求的執行鏈 HandlerExecutionChain(Handler + 攔截器)
  2. 呼叫getHandlerAdapter()獲取能夠執行Handler的介面卡
  3. 介面卡呼叫Handler執行ha.handle(),總會返回一個ModelAndView物件
  4. 呼叫processDispatchResult()方法完成檢視跳轉
//org.springframework.web.servlet.DispatcherServlet
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HttpServletRequest processedRequest = request;
		//執行器鏈,包含了handler和一些攔截器
		HandlerExecutionChain mappedHandler = null;
		boolean multipartRequestParsed = false;
		//非同步管理器
		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

		try {
			ModelAndView mv = null;
			Exception dispatchException = null;

			try {
				//1. 檢查是否是檔案上傳的請求
				processedRequest = checkMultipart(request);
				multipartRequestParsed = (processedRequest != request);

				// Determine handler for the current request.
				/*
					2. 取得處理當前請求的Controller,這裡也稱為Handler,即處理器。這裡並不是直接返回controller,
					而是返回HandlerExecutionChain  請求處理鏈物件 該物件封裝了Handler和Inteceptor
				 */
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null) {
					// 如果handler為空, 則返回404
					noHandlerFound(processedRequest, response);
					return;
				}

				// Determine handler adapter for the current request.
				// 3. 獲取處理請求的處理器介面卡 HandlerAdapter
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

				// Process last-modified header, if supported by the handler.
				// 處理last-modeified 請求頭
				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;
				}

				// Actually invoke the handler.
				// 4 實際處理器處理請求,返回結果檢視物件
				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) {
			//最終會呼叫HandlerInterceptor的afterCompletion方法
			//========攔截器的第三個攔截時機————檢視頁面渲染完成之後攔截
			triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
		}
		catch (Throwable err) {
			//最終會呼叫HandlerInterceptor的afterCompletion方法
			//========攔截器的第三個攔截時機————檢視頁面渲染完成之後攔截
			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);
				}
			}
		}
	}

三,getHandler()方法分析

	@Nullable
	protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        //handlerMappings是一個 ArrayList ———— List<HandlerMapping> handlerMappings
        //而HandlerMapping 則儲存了url和handler的對映關係
		if (this.handlerMappings != null) {
            /**
			 * 遍歷 handlerMappings :
			 * 	1. BeanNameUrlHandlerMapping ,早期的一種使用方式
			 * 	2. RequestMappingHandlerMapping,我們的請求所使用的
			 */
			for (HandlerMapping mapping : this.handlerMappings) {
				HandlerExecutionChain handler = mapping.getHandler(request);
				if (handler != null) {
					return handler;
				}
			}
		}
		return null;
	}

image-20201207231151642

HandlerExecutionChain 包含了 DemoController.handle01 以及 0個 interceptors

Q:HandlerMapping裡面的對映關係是在何時進行初始化的?

A:在容器啟動時時,IOC容器在掃描@Controller物件時會掃描@RequestMapping註解,然後就可以建立url和handler方法的對映關係


四,getHandlerAdapter()方法——介面卡獲取分析

	protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
        //List<HandlerAdapter> handlerAdapters
		if (this.handlerAdapters != null) {
            /*
				遍歷 handlerAdapters:
				遍歷各個HandlerAdapter,看哪個Adapter⽀持處理當前Handler:
				handlerAdapters是一個List<HandlerAdapter>,HandlerAdapter是一個介面,裡面有幾個實現類:
				1. HttpRequestHandlerAdapter ——實現介面的方式
				2. SimpleControllerHandlerAdapter ——實現Controller介面的方式
				3. RequestMappingHandlerAdapter ——是不是HandlerMethod(RequestMappingHandlerMapping封裝的)的例項
			 */
			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");
	}

image-20201207232028836

Q:handlerAdapters 中三個 實現類是如何初始化的?

A:這些元件簡單的說也是容器啟動時初始化的。


五,SpringMVC九大元件初始化

5.1 九大元件

//org.springframework.web.servlet.DispatcherServlet	
	
//多部件解析器,檔案上傳之類的
@Nullable
private MultipartResolver multipartResolver;

//區域化 國際化解析器
@Nullable
private LocaleResolver localeResolver;

//主題解析器
@Nullable
private ThemeResolver themeResolver;

//處理器對映器元件
@Nullable
private List<HandlerMapping> handlerMappings;

//處理器介面卡元件
@Nullable
private List<HandlerAdapter> handlerAdapters;

//異常解析器
@Nullable
private List<HandlerExceptionResolver> handlerExceptionResolvers;

//預設檢視名轉換器元件
@Nullable
private RequestToViewNameTranslator viewNameTranslator;

//flash屬性管理元件
@Nullable
private FlashMapManager flashMapManager;

//檢視解析器
@Nullable
private List<ViewResolver> viewResolvers;

上述九大元件都是定義了介面,介面其實是定義了規範。

5.2 九大元件初始化細節:

//org.springframework.web.servlet.DispatcherServlet

//主要完成元件的初始化
@Override
protected void onRefresh(ApplicationContext context) {
	//初始化策略
	initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
   // 多檔案上傳的元件
   initMultipartResolver(context);
   // 初始化本地語語言環境
   initLocaleResolver(context);
   // 初始化模板處理器
   initThemeResolver(context);
   // 初始化HandlerMapping
   initHandlerMappings(context);
   // 初始化引數介面卡
   initHandlerAdapters(context);
   // 初始化異常攔截器
   initHandlerExceptionResolvers(context);
   // 初始化檢視前處理器
   initRequestToViewNameTranslator(context);
   // 初始化檢視轉換器
   initViewResolvers(context);
   // 初始化FlashMap 管理器
   initFlashMapManager(context);
}

onRefresh方法何時被呼叫?

在此方法中打一個斷點,然後Debug模式啟動,然後觀察呼叫棧。

image-20201207234611575
image-20201207234743923

image-20201207235026553

可以看到,最初是由refresh方法呼叫的——finishRefresh(),由finishRefresh()釋出事件,然後觸發事件監聽,最終到了onRefresh方法。

重點來看:
image-20201207235422018

5.2.1 initHandlerMappings(context)

image-20201208001420462

image-20201208001631859

預設的配置:

image-20201208001800986

initHandlerAdapters同理

***注意 多檔案上傳的元件(MultipartResolver)必須按照id註冊物件:

image-20201208002001922

六,Handler方法細節刨析:

//org.springframework.web.servlet.DispatcherServlet#doDispatch

// 4 實際處理器處理請求,返回結果檢視物件
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

image-20201208211550878
image-20201208220634075

image-20201208220646748

image-20201208220653618

image-20201208220659607

七,processDispatchResult方法

//跳轉檢視頁面,渲染檢視
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

render方法完成渲染

image-20201208221146139

image-20201208221310872

相關文章