當大家瞭解瞭如何編寫一個簡單的Spring MVC程式後,大家心中應該會有一些好奇:這背後到底發生了什麼?
Spring MVC是怎麼把這些功能串聯起來的?我們只是寫了一個控制器而已,HTTP請求是怎麼轉換為控制器方法的呼叫的?結果又是怎麼變成JSON的.....啊這小夥伴們是不是已經混亂了!?
接下來讓我們看看這背後究竟發生了什麼。
現代Java Web專案在處理HTTP請求時基本都遵循一樣的規範,即Java Servlet規範(JSR 340)。其處理流程都是Servlet容器(例如Tomcat或Jetty)收到一個請求,接著找到合適的Servlet進行處理,隨後返回響應。
在SpringMVC中,這個處理請求的Servlet就是前面提到過的DispatcherServlet。
根據設定,Servlet容器會將指定的請求都交由它來處理,在收到請求後,DispatcherServlet會在Spring容器中找到合適的處理器(大部分情況下是控制器,即帶有@Controller註解的類)來處理請求,處理結果經過檢視模版後得到呈現(render)的響應內容,最後再返回給使用者,具體流程如下圖所示:
Servlet繼承圖
既然DispatcherServlet是一個Servlet的實現,那就會遵循其生命過程,例如會在建立後進行初始化。
HttpServletBean.init()方法呼叫了子類的方法FrameworkServlet.initServletBean(),其中做了Web應用上下文的初始化,用的就是initWebApplicationContext()。
在初始化Web應用上下文或者是上下文更新時,都會呼叫DispatcherServlet.onRefresh(),而這個方法就一句話,直接呼叫initStrategies(),就是在初始化Spring MVC的很多特殊型別的Bean
Spring MVC中的特殊Bean型別
Bean型別 | 說明 |
---|---|
MultipartResolver | 用來解析Multipart請求的解析器,通常是上傳檔案的請求,MultipartResolver這層抽象的背後會有多種實現,例如基於Commons FileUpload |
LocaleResolver | 和語言環境有關的解析器,通常用於國際化相關的場景中,包含時區、語言等多種資訊 |
ThemeResolver | 主題(Theme)解析器,選擇應用程式的外觀介面,主題通常是一組靜態資源 |
HandlerMapping | 用於將請求對映到處理器上,過程中還包括各種前置與後置處理,兩個主要的實現類是RequestMappingHandlerMapping和SimpleUrlHandlerMapping |
Handler Adapter | 用於觸發執行處理器,通過這層抽象,DispatcherServler可以不用關心具體如何執行呼叫 |
HandlerExceptionResolver | 例外處理解析器 |
ViewResolver | 用於將字串形式的檢視名稱轉化為具體的View,RequestToViewNameTranslator會根據請求資訊轉換對應的檢視名稱 |
FlashMapManager | 用於存取在請求暫存的輸入與輸出資訊,通常會用在重定向時 |
DispatcherServlet在收到請求後,會交由doService()方法來進行處理,其中包含了兩個主要的步驟:
在這裡說一下,DispatcherServlet用到的設計模式是委派模式
委派模式:幹活算你的(普通員工),功勞算我的(一些專案經理,他們不幹活!)
例如:老闆(Boss)給專案經理(Leader)下達任務,專案經理會根據實際情況給每個員工派發任務,待員工把任務完成後,再由專案經理向老闆彙報結果
下表是doService()方法中設定到HttpServletRequest裡的幾個屬性:
屬性名 | 說明 |
---|---|
WEB_APPLICATION_CONTEXT_ATTRIBUTE | WebApplicationContext,Web的應用上下文 |
LOCALE_RESOLVER_ATTRIBUTE | 處理請求時可能會需要用到的LocaleResolver,如果沒有國際化需求,可以忽略它 |
THEME_RESOLVER_ATTRIBUTE | 用來決定使用哪個顯示主題的ThemeResolver,如果沒有這個需求,也可以忽略它 |
THEME_SOURCE_ATTRIBUTE | 用來獲取主題的ThemeSource,預設是當前的WebApplicationContext |
INPUT_FLASH_MAP_ATTRIBUTE | 上個請求傳遞過來暫存到FlashMapManager裡的FlashMap |
OUTPUT_FLASH_MAP_ATTRIBUTE | 用來向後傳遞的FlashMap中的暫存資訊 |
FLASH_MAP_MANAGER_ATTRIBUTE | 如果當前存在FlashMapManager,則將它設定到請求裡 |
doDispatch()方法的大致處理邏輯如下圖,DispatcherServlet會嘗試根據請求來找到合適的處理器,再通過HandlerAdapter來執行處理器的邏輯,經過前置處理、處理器處理和後置處理等多個步驟,最終返回一ModelAndView。
Request MappingHandlerAdapter是專門用來呼叫@RequestMapping註解標記的處理器的。在處理結果的那一步,如果有異常就處理異常,例如交給專門的HandlerExceptionResolver來處理;如果沒有異常就看ModelAndView,不為空則呈現具體的檢視,不存在也不用擔心,因為請求可能已經處理完成了。
在呼叫處理器邏輯處理的過程中,針對方法的返回值,會呼叫HandlerMethodReturnValueHandler進行處理——根據不同的情況,會有不同實現來做處理。
例如,加了@ResponseBody的方法,返回值就直接被RequestResponseBodyMethodProcessor處理掉了,選擇合適的HttpMessageConverter將物件直接序列化為相映的內容;而返回字串作為檢視名的情況,則是由ViewNameMethodReturnValueHandler來處理的。