1. 程式人生 > >Spring3.1.0實現原理分析(十四).MVC之處理器對映

Spring3.1.0實現原理分析(十四).MVC之處理器對映

       大家好,今天我們分析下處理器對映,這個功能是MVC框架所應具備的基本功能。那麼,什麼是處理器對映呢,是指根據一套規則獲取處理本次request請求的執行鏈物件,它是連線url請求和執行鏈物件的橋樑。執行鏈又是什麼東東呢?無論是spring mvc還是struts2,執行鏈往往就是若干個攔截器加一個處理請求的方法物件(又被稱為處理器)。

       spring在啟動過程中會自動註冊兩個處理器對映物件,分別是“RequestMappingHandlerMapping”和“BeanNameUrlHandlerMapping”,前者的優先順序最高,優先使用RequestMappingHandlerMapping來處理請求,只有在其無法獲取請求的處理器情況下才會使用其它對映器。所以今天我主要就是分析下RequestMappingHandlerMapping物件。

       照例,還是先上一張類結構圖,然後我從初始化和處理請求兩個方面來講解。

一. 攔截器分類和註冊處理方法物件

        從上圖可以看出處理器對映實現了ApplicationContextAware介面,在建立處理器對映過程中的初始化bean階段,spring系統自動註冊的bean後處理器

ApplicationContextAwareProcessor會呼叫setApplicationContext(ac) 方法,為處理器對映物件賦值容器物件。就是在這個方法中處理器對映獲取了容器之後執行攔截器分類和註冊處理方法這些操作。 具體實現上,通過重寫的方式,AbstractHandlerMapping, AbstractHandlerMethodMapping, WebApplicationObjectSupport 這三個類共同實現了setApplicationContext(ac) 方法。

1. AbstractHandlerMapping負責攔截器分類,它會把攔截器分成兩類,一類對所有請求都適用,一類只對特定路徑的請求適用,分別置入以下兩個成員變數。

private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<HandlerInterceptor>();

private final List<MappedInterceptor> mappedInterceptors = new ArrayList<MappedInterceptor>();
那麼,攔截器都是從哪裡來的呢?使用<mvc:interceptor>標籤配置的攔截器,spring啟動時會被解析成MappedInterceptor型別攔截器,然後被置入bean工廠,AbstractHandlerMapping就是從bean工廠獲取到這些攔截器;還有一種途徑是,使用者配置注入到AbstractHandlerMapping的成員變數interceptors,這個變數的型別是List<Object>,其支援的攔截器型別有“HandlerInterceptor”,“WebRequestInterceptor”,“MappedInterceptor”。

2. AbstractHandlerMethodMapping負責註冊所有處理器的處理方法,其過程如下:

       1. 預設從子web容器中獲取所有bean。

       2. 找出其中的處理器bean,即bean是否被@Controller註解了。

       3. 從處理器bean中找出所有處理方法,什麼樣的方法物件是處理方法呢,即該方法是否被@RequestMapping註解了。發現處理方法後把它的@RequestMapping註解解析成RequestMappingInfo物件,再把方法物件包裝成HandlerMethod物件。關於RequestMappingInfo可以看這篇部落格《Spring3.1.0實現原理分析(十三).MVC請求對映資訊RequestMappingInfo詳解》

      4. 把RequestMappingInfo和HandlerMethod物件物件置入快取handlerMethods。

      5. 從RequestMappingInfo中獲取非模式的請求路徑集合,把非模式請求路徑和RequestMappingInfo置入快取urlMap。

3. WebApplicationObjectSupport負責從web容器獲取servletContext賦值給成員變數。

二. 獲取執行鏈物件

獲取執行鏈物件是HandlerMapping介面定義的唯一方法,執行鏈其實就是若干個攔截器和一個處理器構成的。我們可以看下該類的原始碼定義,

public class HandlerExecutionChain 
{
	/**
	 * 處理器
	 */
	private final Object handler;
	/**
	 * 攔截器列表
	 */
	private List<HandlerInterceptor> interceptorList;

	....
}
攔截器是如何獲取的呢?

       上文說了,啟動過程中AbstractHandlerMapping會把bean工廠中的攔截器分成兩類,一類適用於所有請求,一類適用於特定路徑的請求。那麼,適用於所有請求的攔截器肯定是要返回的;另一類適用特定路徑的攔截器的型別是MappedInterceptor,它持有適用該攔截器的模式請求路徑陣列,如果當前請求的路徑跟其中某個元素匹配,則返回該攔截器。

處理器如何獲取呢?

1. 把請求路徑作為key,嘗試直接從快取urlMap中獲取RequestMappingInfo物件,urlMap中快取的是非模式請求路徑和RequestMappingInfo的對應關係。也就是說先把請求路徑當成非模式路徑來處理,如果能夠獲取到RequestMappingInfo對,則把RequestMappingInfo做為key,從handlerMethods中獲取處理器。

2. 如果步驟1未成功,則遍歷handlerMethods的所有key,handlerMethods的key是RequestMappingInfo物件,從中找出所有能跟當前請求匹配的RequestMappingInfo物件。具體如何判斷是否匹配,請看上篇部落格《MVC請求對映資訊RequestMappingInfo詳解》。如果最終有多個RequestMappingInfo物件匹配當前請求,則選出匹配度最高的RequestMappingInfo物件,最終根據RequestMappingInfo從快取handlerMethods中獲取HandlerMethod物件。

3. 獲取了HandlerMethod物件後,還會執行幾個比較重要的操作,如,

    a. 把請求路徑置入request物件的屬性集,key是“HandlerMapping.pathWithinHandlerMapping”。

    b. 從請求路徑中提取模板變數,置入request物件的屬性集,key是“HandlerMapping.uriTemplateVariables”。

4. 如果步驟1和步驟2都不成功,則返回null。