SpringMVC底層原理

筆者的專業是軟體技術,主修java,記得剛開始寫web應用的時候,都是直接寫Servlet,有多少個請求服務就寫多少個Servlet,於是一個系統中出現了一堆的Servlet,記得那會jsp也很流行,後來又經歷了Struts1、Struts2,到現在前後端技術分離了,則更多是用SpringMVC。隨著技術的發展,你會發現寫程式碼變得越來越簡單,當然這個簡單是建立在前輩大神們深邃的設計思想上的,今天我們就來詳細聊聊SpringMVC,學習SpringMVC底層原理的同時,感受下大神們的設計思想。

我們先來透過一張圖瞭解下SpringMVC處理請求的整體流程:

SpringMVC底層原理

使用過SpringMVC的老鐵們都知道,在SpringMVC中最核心的就是DispatcherServlet類,接下來筆者將從DispatcherServlet如何處理請求的整體流程來闡述它的底層實現。

DispatcherServlet毫無疑問是一個HttpServlet,居於此可以追蹤到所有的請求都會進入到doDispatch方法中,而這個方法就是咱們要解剖的方法:

SpringMVC底層原理

該方法主要有幾條主線:獲取HandlerExecutionChain、獲取HandlerAdapter、呼叫Adapter的handle方法、檢視處理器處理。下面我們將從這幾個主線逐一分析。

一、獲取HandlerExecutionChain

首先說明下這個HandlerExecutionChain,它裡面封裝了一堆的Interceptor攔截器,以及Handler,它是一個處理鏈,透過getHandler方法獲取得到:

SpringMVC底層原理

從程式碼中我看看到,這裡迴圈handlerMappings,呼叫HandlerMapping的getHandler方法來獲取HandlerExecutionChain物件,獲取到了就返回,那這個handlerMappings都包含了哪些HandlerMapping呢?它們是什麼時候被塞到handlerMappings集合中去的?

SpringMVC底層原理

在DispatcherServlet的initStrategies方法中,會初始化一堆的資料,其中就有呼叫initHandlerMappings方法來初始化HandlerMapping,放到handlerMappings集合中,至於initStrategies方法是怎麼被呼叫的,大家看下DispatcherServlet的繼承結構圖,然後根據Servlet的生命週期跟蹤下相信就知道了。

SpringMVC底層原理

我們來看下initHandlerMappings方法:

SpringMVC底層原理

從方法中可以看出,從Spring的Bean工廠中獲取HandlerMapping。class型別的Bean,以得到handlerMappings,在實際過程中,和咱們實際應用相關的HandlerMapping主要包括:BeanNameUrlHandlerMapping、SimpleUrlHandlerMapping、RequestMappingHandlerMapping這三類,當然還有其他。

所以我們就知道了,上面是呼叫上面這三個HandlerMapping的getHandler方法來獲取HandlerExecutionChain物件。我們大概猜想,這個getHandler方法是根據請求的URL路徑,來獲取到處理的物件或者方法,這個是順理成章的,因為我們在開發的時候,就是透過配置path路徑來明確請求的路徑和方法或類的對應關係的。

我們先來看它們內部到底存放了什麼,以及getHandler方法的具體邏輯,我們主要看BeanNameUrlHandlerMapping和RequestMappingHandlerMapping。

BeanNameUrlHandlerMapping

SpringMVC底層原理

從它的繼承關係圖中可以看到,它是Aware的實現類,跟蹤它的生命週期,Spring會呼叫initApplicationContext方法,然後呼叫detectHandlers方法來找到對應的Handler,並呼叫registerHandler方法將找到的Handler新增到handlerMap這個Map集合中去。

SpringMVC底層原理

那上面“對應的Handler”需要滿足什麼條件呢?

SpringMVC底層原理

看上面的判斷邏輯,表明如果bean的名稱是以“/”開頭,則滿足條件,然後從Spring的Bean工廠中獲取到對應的Bean例項,新增到handlerMap集合。

在具體使用時,可以實現Controller和HttpRequestHandler介面,同時Component註解的value值以“/”開頭。

好,資料準備完成,接下來就是getHandler方法:

SpringMVC底層原理

SpringMVC底層原理

SpringMVC底層原理

跟蹤程式碼可以看出,就是根據URL從handlerMap獲取到對應的例項,隨後再將handler和HandlerInterceptor封裝成HandlerExecutionChain物件:

SpringMVC底層原理

RequestMappingHandlerMapping

SpringMVC底層原理

RequestMappingHandlerMapping是InitializingBean的實現類,在bean的初始化階段,它的afterPropertiesSet方法會被Spring呼叫,跟蹤該方法:

SpringMVC底層原理

發現獲取所有的bean例項,然後迴圈呼叫processCandidateBean方法:

SpringMVC底層原理

必須滿足一定條件的例項才會被處理,這個條件就是類上面包含Controller或者RequestMapping註解:

SpringMVC底層原理

SpringMVC底層原理

對於滿足上述條件的bean,會在detectHandlerMethods方法中將RequestMapping註解中的路徑和對應的方法封裝成HandlerMethod,並新增到mappingLookup集合中。

然後呼叫getHandler方法,根據請求URL從mappingLookup集合中取出HandlerMethod,並封裝成HandlerExecutionChain物件。

SpringMVC底層原理

二、獲取HandlerAdapter

在上述呼叫getHandler方法,獲取到HandlerExecutionChain物件後,接下來呼叫getHandlerAdapter方法獲取HandlerAdapter:

SpringMVC底層原理

迴圈所有的HandlerAdapter,呼叫supports方法判斷HandlerAdapter是否支援處理Handler。這裡有兩個問題:第一、HandlerAdapter有哪些?它們是什麼時候被初始化的?第二、每個HandlerAdapter的supports方法的具體實現;

HandlerAdapter有哪些?它們是什麼時候被新增到handlerAdapters的?

同上,HandlerAdapter的初始化也是在initStrategies方法中發起的,在initHandlerAdapters方法中完成具體的新增:

SpringMVC底層原理

可以看到,從Spring容器中獲取所有的HandlerAdapter型別的Bean新增到handlerAdapters中,預設情況下包括:

RequestMappingHandlerAdapter、HandlerFunctionAdapter、HttpRequestHandlerAdapter、SimpleControllerHandlerAdapter四個實現類的Bean例項。

HandlerAdapter的supports方法的具體實現

我們這裡主要講下RequestMappingHandlerAdapter、SimpleControllerHandlerAdapter和HttpRequestHandlerAdapter的supports實現邏輯:

1.RequestMappingHandlerAdapter

SpringMVC底層原理

SpringMVC底層原理

Handler是HandlerMethod型別,則由RequestMappingHandlerAdapter處理。

2.SimpleControllerHandlerAdapter

SpringMVC底層原理

如果handler是Controller的實現類,則會由SimpleControllerHandlerAdapter處理。

3.HttpRequestHandlerAdapter

如果handler是HttpRequestHandler的實現類,則會由HttpRequestHandlerAdapter處理。

SpringMVC底層原理

三、呼叫HandlerAdapter的handle方法

在獲取到HandlerAdapter後,還會呼叫interceptor的preHandle方法,這裡就不詳細描述了。這裡咱們直接看HandlerAdapter的handle方法的具體實現。

1.RequestMappingHandlerAdapter

相對於其他Adapter的處理方法,RequestMappingHandlerAdapter要複雜的的多

SpringMVC底層原理

顯示建立ServletInvocableHandlerMethod物件,然後往物件中新增HandlerMethodArgumentResolvers和HandlerMethodReturnValueHandlers,這兩個東西很重要,是SpringMVC的兩個重要擴充套件點。

隨後開始處理,總結來說主要做了兩件事:

SpringMVC底層原理

獲取方法引數值,然後呼叫方法:

SpringMVC底層原理

執行方法相對簡單,咱們主要來看看是如何獲取方法引數值的:

SpringMVC底層原理

先獲取到方法上的所有引數資訊MethodParameter,然後呼叫resolvers。supportsParameter方法來判斷是否支援對引數型別進行轉換,那這個resolvers是啥?它是一個HandlerMethodArgumentResolver,裡面包含了一堆的HandlerMethodArgumentResolver,而這個HandlerMethodArgumentResolver就是專門負責引數轉換用的:

SpringMVC底層原理

除了SpringMVC自己提供的HandlerMethodArgumentResolver外,還支援讓咱們自己來提供,只要實現HandlerMethodArgumentResolver即可。SpringMVC透過呼叫HandlerMethodArgumentResolver類的supportsParameter方法來找到一個適合處理的HandlerMethodArgumentResolver,找到了合適的Resolver後,呼叫它的resolveArgument方法來進行引數轉換,最終得到所有的引數值。

對返回值進行處理

在呼叫方法完成後,如果有返回值,則呼叫returnValueHandlers。handleReturnValue來處理返回值,這個returnValueHandlers是HandlerMethodReturnValueHandler型別的例項,它包含了一堆的HandlerMethodReturnValueHandler,HandlerMethodReturnValueHandler就是專門處理返回值的實現類,除了預設的HandlerMethodReturnValueHandler外,SpringMVC還允許咱們自己實現HandlerMethodReturnValueHandler。

SpringMVC底層原理

首先SpringMVC會獲取一個最合適的HandlerMethodReturnValueHandler:

SpringMVC底層原理

選擇的邏輯就是迴圈呼叫所有HandlerMethodReturnValueHandler的supportsReturnType方法,返回為true的就是最合適的:

SpringMVC底層原理

得到HandlerMethodReturnValueHandler後,呼叫它的handleReturnValue方法來完成返回值的處理。

2.SimpleControllerHandlerAdapter

SpringMVC底層原理

SimpleControllerHandlerAdapter的handle方法,就是執行Controller實現類的handleRequest方法。

3.HttpRequestHandlerAdapter

SpringMVC底層原理

HttpRequestHandlerAdapter的handle方法,就是執行HttpRequestHandler實現類的handleRequest方法。

四、檢視渲染

上述完成Adapter的handle方法後,會執行過濾器HandlerInterceptor的postHandle方法,這裡不再描述。如果返回值是ModelAndView,則會呼叫processDispatchResult,來完成檢視渲染:

SpringMVC底層原理

SpringMVC底層原理

這裡會先得到以一個View,也就是檢視器,然後呼叫view的render方法來完成渲染處理。那核心點就是如何獲取這個View。

SpringMVC底層原理

迴圈呼叫viewResolvers中ViewResolver的resolveViewName方法,得到最合適的View。預設情況下SpringMVC提供了四種類型的View:BeanNameViewResolver、ViewResolverComposite、InternalResourceViewResolver、ContentNegotiatingViewResolver,當然咱們也可以自己實現ViewResolver,從而新增自己的View。