解讀SpringMVC源碼

自從Struts2徹底退出江湖之後,Spring MVC已經成爲了表現層框架的扛把子。今天,我們就來窺探一下它的世界。

本文圍繞Spring MVC的前端控制器DispatcherServlet展開,先介紹了它的類繼承關係和初始化方法,然後介紹Spring MVC的九大組件和運行流程,最後帶領大家分析從請求到響應的相關處理環節代碼。

一、Spring MVC框架的接入點

最開始使用Spring MVC的時候,我們都在Java Web項目的web.xml文件寫過這個東西(使用Spring Boot或者註解的請自動忽略)。

當請求的路徑匹配到url-pattern時,就交由名爲servlet-name的Servlet處理。這個時候,Spring MVC的前端控制器DispatcherServlet開始接手了。

二、 DispatcherServlet的繼承關係

頂層接口Servlet提供了5個接口:init、getServletConfig、service、getServletInfo、destroy。

我們主要講下init方法和service方法。但是由於service方法是核心,放到後面去重點講,所以就簡單說一下init方法。

官方解釋:在實例化servlet之後,由servlet容器僅調用一次,以向servlet指示正在投入使用,在servlet接收到任何請求之前完成。

Spring MVC的DispatcherServlet在這個階段要完成的事情有哪些呢?

由於GenericServlet的init什麼都沒做,由子類重寫,所以初始化的工作由HttpServletBean-->FrameworkServlet-->DispatcherServlet逐級完成。

1、首先是爺爺類HttpServletBean:將配置參數映射到此servlet的bean屬性上,以及調用子類的初始化(就是initServletBean方法)。

2、然後是DispatcherServlet的父類FrameworkServlet:在bean的任何屬性被設置之後(HttpServletBean完成),調用重寫的方法initServletBean,創建此servlet的WebApplicationContext。

initWebApplicationContext()方法代碼就不貼了。值得注意的是,它會執行一個默認實現爲空,由子類重寫的方法onRefresh。

3、最後就是DispatcherServlet了:重寫onRefresh方法,初始化Spring MVC傳說中的九大組件。

三、Spring MVC的九大組件

來簡單介紹一下九大組件的作用

  1. MultipartResolver:用於處理上傳請求。處理方法是將普通的request包裝成MultipartHttpServletRequest,後者可以直接調用getFile方法獲取File。
  2. LocaleResolver:SpringMVC主要有兩個地方用到了Locale:一是ViewResolver視圖解析的時候;二是用到國際化資源或者主題的時候。
  3. ThemeResolver:用於解析主題。SpringMVC中一個主題對應一個properties文件,裏面存放着跟當前主題相關的所有資源,如圖片、css樣式等。SpringMVC的主題也支持國際化。
  4. HandlerMapping:用來查找Handler的。(可以多個)
  5. HandlerAdapter:從名字上看,它就是一個適配器。Servlet需要的處理方法的結構卻是固定的,都是以request和response爲參數的方法。如何讓固定的Servlet處理方法調用靈活的Handler來進行處理呢?這就是HandlerAdapter要做的事情。(可以多個)
  6. HandlerExceptionResolver:其它組件都是用來幹活的。在幹活的過程中難免會出現問題,出問題後怎麼辦呢?這就需要有一個專門的角色對異常情況進行處理,在SpringMVC中就是HandlerExceptionResolver。(可以多個)
  7. RequestToViewNameTranslator:有的Handler處理完後並沒有設置View也沒有設置ViewName,這時就需要從request獲取ViewName了,如何從request中獲取ViewName就是RequestToViewNameTranslator要做的事情了。
  8. ViewResolver:ViewResolver用來將String類型的視圖名和Locale解析爲View類型的視圖。View是用來渲染頁面的,也就是將程序返回的參數填入模板裏,生成html(也可能是其它類型)文件。(可以多個)
  9. FlashMapManager:用來管理FlashMap的,FlashMap主要用在redirect重定向中傳遞參數。

四、Spring MVC的運行流程

看着上面的九大組件是不是一臉懵逼?沒關係,圖文並茂帶你消化,重點來了

圖片很精彩,但是鮮花也要綠葉相伴。針對圖中的每一步驟做下解釋

  1. 用戶發送請求Request至前端控制器DispatcherServlet。
  2. DispatcherServlet遍歷它的所有處理器映射器HandlerMapping,每一個HandlerMapping根據請求url找到具體的處理器Handler。如果找到了,將Handler對象封裝成處理器執行鏈HandlerExecutionChain,並添加處理器攔截器HandlerInterceptor(如果有),然後將其返回給DispatcherServlet。
  3. DispatcherServlet根據HandlerExecutionChain的Handler找到合適的處理器適配器HandlerAdapter。
  4. DispatcherServlet通過HandlerAdapter去調用真正的處理器(後端控制器)Controller。
  5. Controller執行完成返回視圖模型ModelAndView給HandlerAdapter,HandlerAdapter將其返回給DispatcherServlet。
  6. DispatcherServlet將ModelAndView傳給視圖解析器ViewReslover,ViewReslover解析後返回具體視圖View。
  7. DispatcherServlet對View進行渲染視圖(即將模型數據填充至視圖中)。
  8. DispatcherServlet響應Response給用戶。

從上面可以看出,DispatcherServlet充當一個全局控制器的角色,有接收請求,響應結果,轉發等作用。有了DispatcherServlet之後,可以減少組件之間的耦合度

五、看源碼看源碼看源碼

這裏,我們使用一個get請求(對應處理器使用Controller和RequestMapping註解)加一個自定義處理器攔截器來示範。

1、請求怎麼進入核心方法doDispatch的?

一個請求來了,毫無疑問先調用的是Servlet的service方法,官方對其解釋:Called by the servlet container to allow the servlet to respond to a request. 翻譯就是:由servlet容器調用,以允許servlet響應一個請求。

但是在DispatcherServlet沒有找到service方法哎,怎麼辦?沒關係,它老子FrameworkServlet有,看吧

除了PATCH方式的請求外,其它委託父類處理,但是HttpServletBean沒有重寫該方法,所以就到了HttpServlet處理。但是,這玩意沒做什麼處理,官方對這個方法的解釋:接收來自public的service方法的標準HTTP請求,並將其分派給定義在這個類裏面的指定請求方式的方法處理;這個service方法是HTTP協議特定版本的實現,沒有必要重寫。屁,PATCH方式你們不就不支持嘛。

所以,HttpServlet的service方法的作用就是:針對不同的http請求方式,將其交給對應的方法處理。至於對應的方法,由子類重寫。

這下,回到了FrameworkServlet的doGet方法,它又委派給processRequest方法處理。仔細瞄了一下,其它的doPost、doPut、doDelete等方法都是如此。

然後processRequest又做了一些事情(大概是記錄一下和處理requestAttributes什麼的),就交給doService方法了。

這下,終於回到了我們收悉的DispatcherServlet類了,不容易啊。我們只關注裏面的調用doDispatch方法,其它的看註釋好了。

進入doDispatch方法,纔算真正來到了我們Spring MVC運行流程圖的第1步。先把這個方法的核心源碼貼上,壓一下經

2、判斷是否爲文件上傳的請求

首先是checkMultipart方法,這個方法是檢查是否是二進制的請求(文件上傳的請求)。是的話轉爲multipart request,否則返回原request。我們這裏不是,所以multipartRequestParsed是false。

3、高潮1:遍歷處理器映射器查找匹配的處理器

獲取處理器,但是返回值爲什麼是處理器執行鏈HandlerExecutionChain?因爲除了要執行處理器(Controller)之外,可能還有若干攔截器Interceptor。

怎麼查找處理器呢,先遍歷註冊的HandlerMappings(我使用的是Spring Boot默認的,一共有第二張圖裏面的7個)。然後按順序每一個HandlerMapping去根據請求找合適的處理器Handler,誰先找到合適的之後,就繼續找符合的攔截器Interceptor,然後轉成一個HandlerExecutionChain對象返回。後續的HandlerMapping也不再找了。

 

因爲我的處理器是用Controller和RequestMapping註解的,所以最終被RequestMappingHandlerMapping匹配到了。下面貼一下它的查找過程,不做解釋。

 

4、高潮2:根據處理器獲取處理器適配器

跟獲取Handler一樣,遍歷處理器適配器,看哪個先支持處理器就返回誰。我的Controller是被RequestMappingHandlerAdapter匹配到了。

 

 

 

5、執行所有攔截器的preHandle方法

只要有一個返回false,就停止往下執行。

 

這個是我自定義的處理器攔截器 

 

6、高潮3:調用處理器

invokeHandlerMethod方法主要是用到了反射,代碼就不貼了。

 

7、設置默認視圖名稱和執行攔截器的postHandle 方法

applyDefaultViewName:如果mv爲空,就設置默認viewName(如果有的話)。

applyPostHandle:執行所有攔截器的後處理回調方法postHandle。

 

8、處理結果

渲染結果和調用全部攔截器的整個請求處理完畢回調方法afterCompletion。

 

 

9、其它處理

異常時調用全部攔截器的整個請求處理完畢回調方法afterCompletion。

做一些資源清理工作。

到此爲止,Spring MVC的運行流程算是過了一遍了。可能有些地方沒有解釋得很深入和很明白,大家可以自行去研究下對應源碼。

後續打算抄襲一下,寫一篇“自己動手寫Spring MVC框架”。

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