一個http請求從客戶端發起,經服務器處理后返回結果給客戶端,本次聚焦於服務器處理過程,搞清楚SpringMvc處理請求的整個過程。
1.最開始的地方:Servlet
服務器接收到請求后由各種容器處理,如tomcat,這些容器在處理過程中會調用Servlet接口中的方法,由文檔可知,在創建該類時會調用init方法,處理請求時會調用service方法,銷毀時會調用destroy方法。
創建Servlet實例、調用生命周期方法的都是類似tomcat容器在處理請求過程中所做的事情,因此首先需要搞清楚的就是init和service到底干了些什么事。
首先來看下整個servlet的繼承體系,其他的可能不熟悉,但是只要使用過SpringMvc的都會知道DispatcherServlet,很多文章會說這是web請求最開始的地方,其本質原因就是因為它繼承了Servlet接口。
當容器啟動時會進行初始化,此時會調用init方法,也就是調用實現類GenericServlet的init方法,該方法又會調用其子類的init方法
此方法中主要獲取並設置了一些初始值,之后SpringBoot項目就會正式啟動。
2.第一次請求到來時
在第一次發起請求時,依然會調用init方法,因為此時容器中很多組件都未初始化,只有在第一次請求時才會初始化
此時會調用FrameworkServlet的initServletBean方法,而控制台也會打印初始化日志
2.1 initWebApplicationContext方法
因為initFrameworkServlet方法為空實現,因此核心邏輯都在initWebApplicationContext方法中,經過一系列判斷后該方法最終會調用onRefresh方法
而FrameworkServlet中onRefresh方法為空實現,接着會調用子類DispatcherServlet的onRefresh方法,而該方法又會調用initStrategies方法,此方法中進行了所有初始化動作
2.2 initStrategies方法
initThemeResolver,initLocaleResolver,initMultipartResolver,initRequestToViewNameTranslator,initFlashMapManager這幾個初始化方法類似,此處以initMultipartResolver為例講解,首先它會在容器中獲取名稱為multipartResolver的實例,如果獲取失敗則會調用getDefaultStrategy方法獲取默認值,multipartResolver默認是空的,因此沒有調用此方法。
initHandlerMappings,initHandlerAdapters,initHandlerExceptionResolvers,initViewResolvers這幾個方法首先會在容器中找到所有對應接口的實現類,然后對其進行排序,之后賦值給響應的屬性變量。
至此整個初始化請求就算全部結束了,因此初始化過程主要分為兩種,一種為只有一個實例的初始化方法,另一種為存在多個實例的初始化方法。
3.后續請求到來時
3.1 service方法
請求到來時容器會調用servlet的service方法進行處理,實際調用GenericServlet的子類HttpServet的service方法,
該方法內主要做了將ServletRequest和ServletReponse轉為HttpServletRequest和HttpServletResponse。
強轉完成后會調用同類中的重載方法進行請求處理,首先會獲取到請求類型,根據類型的不同調用不同的方法進行處理,因此我們可以知道它支持的請求類型總共有:get、head、post、put、delete、options、trace、patch共8種。
接着會調用子類的doGet方法,最后都會調用processRequest方法
3.2 processRequest方法
該方法初始化了ContextHolders之后就會調用doService方法,而此方法又會調用子類DispatcherServlet的方法
3.3 doService方法
該方法主要給request中放入了多個鍵值對,而其值為第一次請求時init方法初始化得到的,把值放入request中后會調用doDispatch方法。
3.4 doDispatch方法
根據文檔描述可知,所有的http請求都會執行該方法,它會獲取到第一個匹配的handler去執行,首先它會檢查該請求是不是文件上傳類型,即包含Multipart數據,之后會調用getHandler方法確定使用哪個handler進行處理,接着會調用getHandlerAdapter方法確定使用哪個handlerAdapte
之后回調用applyPreHandle方法執行攔截器
接着使用確定好的handleradapter執行handle方法,此方法會調用handleInternal方法
3.5 handleInternal和invokeHandlerMethod方法
此方法會調用invokeHandlerMethod方法,該方法首先獲取數據綁定工廠和model工廠
之后判斷參數解析器是否為空,不過不為空則進行參數解析,此處26個解析器就是我們在請求方法中可以使用的所有參數注解
緊接着會判斷返回類型,如果返回類型解析器不為空則進行解析,此處的14個解析器就是我們在請求方法中可以返回的類型
之后就是業務代碼的執行了,執行完成后會對返回值進行處理
因為在請求上標有requestbody注解,因此使用RequestResponseBodyMethodProcessor處理返回值
然后構建請求和響應消息,通過轉換器將值寫給客戶端
首先會獲取客戶端可以接受的所有類型
最后經過一系列的計算后選擇最合適的一種
之后在消息轉換器中選擇最合適的一個使用
之后調用applyPostHandle方法執行攔截器的postHandle方法,至此整個請求過程就已經全部結束了