一文讀懂 spring MVC 請求處理流程(您值得擁有)

引言

springmvc 在web 項目中基本上是必用的,基於servlet的一種控制層框架,我們在使用的過程中往往會碰到各種問題,有時候還沒有思緒解決問題,主要是因爲我們對請求處理的流程不熟悉,不能快速定位都問題所在,所以總會耗費大量時間,各種百度才解決問題。如果你也有這樣的困惑,不妨一起來看下springmvc 的請求處理流程。讓我們能夠快速定位問題。

學習本場 Chat 您將瞭解到:

  1. DispatcherServlet 九大對象介紹
  2. DispatcherServlet 核心方法分析
  3. request 請求處理流程分析
  4. 請求處理流程源碼剖析

DispatcherServlet 九大組件

其中前面三個爲核心組件。

  • HandlerMapping(處理器映射器)

    HandlerMapping 是⽤來查找 Handler 的,也就是處理器,具體的表現形式可以是類,也可以是⽅法。⽐如,標註了@RequestMapping的每個⽅法都可以看成是⼀個Handler。Handler負責具體實際的請求處理,在請求到達後,HandlerMapping 的作⽤便是找到請求相應的處理器Handler 和 Interceptor.

  • HandlerAdapter(處理器適配器)

    HandlerAdapter 是⼀個適配器。因爲 Spring MVC 中 Handler 可以是任意形式的,只要能處理請求即可。但是把請求交給 Servlet 的時候,由於 Servlet 的⽅法結構都是doService(HttpServletRequest req,HttpServletResponse resp)形式的,要讓固定的 Servlet 處理⽅法調⽤ Handler 來進⾏處理,便是 HandlerAdapter 的職責。

  • ViewResolver

    ViewResolver即視圖解析器,⽤於將String類型的視圖名和Locale解析爲View類型的視圖,只有⼀個resolveViewName()⽅法。從⽅法的定義可以看出,Controller層返回的String類型視圖名viewName 最終會在這⾥被解析成爲View。View是⽤來渲染⻚⾯的,也就是說,它會將程序返回的參數和數據填⼊模板中,⽣成html⽂件。ViewResolver 在這個過程主要完成兩件事情:ViewResolver 找到渲染所⽤的模板(第⼀件⼤事)和所⽤的技術(第⼆件⼤事,其實也就是找到視圖的類型,如JSP)並填⼊參數。默認情況下,Spring MVC會⾃動爲我們配置⼀個InternalResourceViewResolver,是針對 JSP 類型視圖的。

  • HandlerExceptionResolver

    HandlerExceptionResolver ⽤於處理 Handler 產⽣的異常情況。它的作⽤是根據異常設置 ModelAndView,之後交給渲染⽅法進⾏渲染,渲染⽅法會將 ModelAndView 渲染成⻚⾯。

  • RequestToViewNameTranslator

    RequestToViewNameTranslator 組件的作⽤是從請求中獲取 ViewName.因爲 ViewResolver 根據ViewName 查找 View,但有的 Handler 處理完成之後,沒有設置 View,也沒有設置 ViewName,便要通過這個組件從請求中查找 ViewName。

  • LocaleResolver

    ViewResolver 組件的 resolveViewName ⽅法需要兩個參數,⼀個是視圖名,⼀個是 Locale。LocaleResolver ⽤於從請求中解析出 Locale,⽐如中國 Locale 是 zh-CN,⽤來表示⼀個區域。這個組件也是 i18n 的基礎。

  • ThemeResolver

  • MultipartResolver

    MultipartResolver ⽤於上傳請求,通過將普通的請求包裝成 MultipartHttpServletRequest 來實現。MultipartHttpServletRequest 可以通過 getFile() ⽅法 直接獲得⽂件。如果上傳多個⽂件,還可以調⽤ getFileMap()⽅法得到Map<FileName,File>這樣的結構,MultipartResolver 的作⽤就是封裝普通的請求,使其擁有⽂件上傳的功能。

  • FlashMapManager

    FlashMap ⽤於重定向時的參數傳遞,⽐如在處理⽤戶訂單時候,爲了避免重複提交,可以處理完 post 請求之後重定向到⼀個 get 請求,這個 get 請求可以⽤來顯示訂單詳情之類的信息。這樣做雖然可以規避⽤戶重新提交訂單的問題,但是在這個⻚⾯上要顯示訂單的信息,這些數據從哪⾥來獲得呢?因爲重定向時麼有傳遞參數這⼀功能的,如果不想把參數寫進URL(不推薦),那麼就可以通過 FlashMap 來傳遞。只需要在重定向之前將要傳遞的數據寫⼊請求(可以通過 ServletRequestAttributes.getRequest()⽅法獲得)的屬性OUTPUT_FLASH_MAP_ATTRIBUTE 中,這樣在重定向之後的Handler中Spring就會⾃動將其設置到Model中,在顯示訂單信息的⻚⾯上就可以直接從Model中獲取數據。FlashMapManager 就是⽤來管理 FalshMap 的。

這九大組件都在 DispatcherServlet 類中。

/** MultipartResolver used by this servlet. */
	// 多部件解析器
	@Nullable
	private MultipartResolver multipartResolver;

	/** LocaleResolver used by this servlet. */
	//地域解析器,國際化
	@Nullable
	private LocaleResolver localeResolver;

	/** ThemeResolver used by this servlet. */
	//主題解析器
	@Nullable
	private ThemeResolver themeResolver;

	/** List of HandlerMappings used by this servlet. */
	//處理器映射器組件
	@Nullable
	private List<HandlerMapping> handlerMappings;

	/** List of HandlerAdapters used by this servlet. */
	//處理器適配器組件
	@Nullable
	private List<HandlerAdapter> handlerAdapters;

	/** List of HandlerExceptionResolvers used by this servlet. */
	//處理器異常解析器
	@Nullable
	private List<HandlerExceptionResolver> handlerExceptionResolvers;

	/** RequestToViewNameTranslator used by this servlet. */
	//視圖名稱轉換器
	@Nullable
	private RequestToViewNameTranslator viewNameTranslator;

	/** FlashMapManager used by this servlet. */
	//flash 組件管理器
	@Nullable
	private FlashMapManager flashMapManager;

	/** List of ViewResolvers used by this servlet. */
	//視圖解析器
	@Nullable
	private List<ViewResolver> viewResolvers;

MVC 結構體系

三層架構:表現層、業務層和持久層

MVC 設計模式:

MVC 全名是 Model View Controller,是 模型(model)-視圖(view)-控制器(controller) 的縮寫, 是⼀

種⽤於設計創建 Web 應⽤程序表現層的模式。MVC 中每個部分各司其職:

Model(模型):模型包含業務模型和數據模型,數據模型⽤於封裝數據,業務模型⽤於處理業

務。

View(視圖): 通常指的就是我們的 jsp 或者 html。作⽤⼀般就是展示數據的。通常視圖是依據

模型數據創建的。

Controller(控制器): 是應⽤程序中處理⽤戶交互的部分。作⽤⼀般就是處理程序邏輯的。

MVC提倡:每⼀層只編寫⾃⼰的東⻄,不編寫任何其他的代碼;分層是爲了解耦,解耦是爲了維

護⽅便和分⼯協作。

spring MVC 請求處理流程

所有請求都會通過DispatcherServlet 來執行。

流程說明:

1、DispatcherServlet 接收到客戶端發送的請求。

2、DispatcherServlet 收到請求調用HandlerMapping 處理器映射器。

3、HandleMapping 根據請求URL 找到對應的handler 以及處理器 攔截器,返回給DispatcherServlet

4、DispatcherServlet 根據handler 調用HanderAdapter 處理器適配器。

5、HandlerAdapter 根據handler 執行處理器,也就是我們controller層寫的業務邏輯,並返回一個ModeAndView

6、HandlerAdapter 返回ModeAndView 給DispatcherServlet

7、DispatcherServlet 調用 ViewResolver 視圖解析器來 來解析ModeAndView

8、ViewResolve 解析ModeAndView 並返回真正的view 給DispatcherServlet

9、DispatcherServlet 將得到的視圖進行渲染,填充到request域中

10、返回給客戶端響應結果。

springmvc 源碼分析

請求進來。先找HttpServlet.發現FrameworkServlet 重現了doGet()和doPost() 方法。所以會執行其中的方法。

image-20200610155343088

並且在FrameworkServlet 中實現了HttpServlet 的 doGet()和doPost() 方法。

image-20200610155417563

我們接着看FrameworkServlet 的doGet和doPost 方法都是調用其processRequest 方法。這個方法裏面主要的調用DispatcherServlet中doService()方法。

image-20200610160254087

在DispatcherServlet 中的這個方法。會調用doDispatche() 方法執行。

image-20200610160509049

image-20200610160104711

所以整體的請求流程都在doDispatch() 方法中。

主要流程如下:

1、根據url 請求獲取到handlerExecutorChina 對象,也就是獲取到handler執行鏈對象,這個對象中包含執行的handler 和一個攔截器的集合。

image-20200611154006184

image-20200611154037844

2、根據這個handlerExecutorChina 執行鏈對象獲取到對應的handlerAdapter handler適配器。

3、根據這個適配器,真正執行handler ,並且返回一個modeAndView 這裏的handler 就是我們通常所有的controller 層的業務邏輯。

image-20200611154638816

4、根據獲取到的modeAndView ,解析渲染頁面並返回結果。

image-20200611160300399

這樣整體核心流程就完了。只有這四步。其實和最開始的流程處理圖是一樣的。總結一下:先根據請求從handlermapping 中獲取到handle ,然後通過handle 找到能處理該請求的 handlerAdapter ,通過handlerAdapter 執行真正的handler 也就是我們的業務代碼,並返回一個 ModeAndView 。然後通過viewResolver 解析成view 返回給頁面

九大組件初始化的流程

九大組件初始化是在 DispatcherServlet 中的onRefresh() 方法中。

image-20200611161600286

我們通過調用棧可以發現,是在applicationContext類中的refresh() 方法中執行的。這個方法我們很熟悉就是spring 框架的初始化流程執行的主要方法。在refresh() 方法中 完成的Bean 對象實例化流程之後,會執行事件發佈。這樣就來到了我們springmvc 初始化了。

這九大組件的初始化流程都是差不多的,我們來看下核心組件的HandlerMapping 組件的初始化。

先在ApplicationContext中查找所有HandlerMappings,包括祖先上下文 活着指定的名稱還獲取,如果獲取不到,就會從默認的配置中獲取handlermappings

image-20200611163342283

獲取默認的handlermappings 主要是從defaultStrategies 中獲取。

那defaultStrategies 數據是怎麼來的呢?

可以看到這個Properties 是從DispatcherServlet.properties 配置文件來的。

image-20200611164132953

我們查看這個問價可以看到默認的配置信息,九大組件的默認配置信息都有。

獲取Handler 流程

下面我們回到主流程,第一步,從handlermappingshandlerMappings 中獲取對應的handler

image-20200611164923494

可以看到就是遍歷handlerMappings 找到這個url 對應的handler 就完了。

獲取handlerAdapter 流程

一樣的遍歷handlerAdpters ,獲取到可以處理這個handler 的適配器。

ha.handler() 執行真正的處理器流程

image-20200611170202252

image-20200611170222629

image-20200611170451679

image-20200611171336268

可以根據調用棧很好的找到調用流程。主要就是檢測request ,然後通過url 找到隊對應的方法,填充方法需要的參數值,通過反射調用該方法。

processDispatchResult 處理流程

render⽅法完成渲染

image-20200611172123379

image-20200611172201908

視圖解析器解析出View視圖對象

image-20200611172219756

2、在解析出View視圖對象的過程中會判斷是否重定向、是否轉發等,不同的情況封裝的是不同的View實現

3、解析出View視圖對象的過程中,要將邏輯視圖名解析爲物理視圖名

4、封裝View視圖對象之後,調⽤了view對象的render⽅法

5、渲染數據

6、把modelMap中的數據暴露到request域中,這也是爲什麼後臺model.add之後在jsp中可以從請求域取出來的根本原因

7、將數據設置到請求域中

總結

主要了解請求處理的流程,在 DispatcherServlet 中,先通過請求從handlerMappings 中獲取對應的handler ,然後通過handler 找到對應的 handlerAdapter 然後通過handlerAdapter 的handle() 方法真正執行我麼controller 層的業務邏輯。並返回一個 ModeAndView 然後通過 ViewResolver 解析成view 並渲染到界面上。

當然這中間還有攔截器,主要在獲取 handlerAdapter 後,會先執行攔截器方法,然後才執行真正的handler 方法。

如果您覺得好看,記得給我點個贊喔!!!

如果您覺得好看,記得給我點個贊喔!!!

如果您覺得好看,記得給我點個贊喔!!!

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