精盡Spring MVC原始碼分析 - HandlerMapping 元件(一)之 AbstractHandlerMapping
阿新 • • 發佈:2020-12-15
> 該系列文件是本人在學習 Spring MVC 的原始碼過程中總結下來的,可能對讀者不太友好,請結合我的原始碼註釋 [Spring MVC 原始碼分析 GitHub 地址](https://github.com/liu844869663/spring-framework) 進行閱讀
>
> Spring 版本:5.2.4.RELEASE
>
> 該系列其他文件請檢視:[**《精盡 Spring MVC 原始碼分析 - 文章導讀》**](https://www.cnblogs.com/lifullmoon/p/14123963.html)
## HandlerMapping 元件
HandlerMapping 元件,請求的**處理器匹配器**,負責為請求找到合適的 `HandlerExecutionChain` 處理器執行鏈,包含處理器(`handler`)和攔截器們(`interceptors`)
- `handler` 處理器是 Object 型別,可以將其理解成 HandlerMethod 物件(例如我們使用最多的 `@RequestMapping` 註解所標註的方法會解析成該物件),包含了方法的所有資訊,通過該物件能夠執行該方法
- `HandlerInterceptor` 攔截器對處理請求進行**增強處理**,可用於在執行方法前、成功執行方法後、處理完成後進行一些邏輯處理
由於 HandlerMapping 元件涉及到的內容比較多,考慮到內容的排版,所以將這部分內容拆分成了四個模組,依次進行分析:
- [**《HandlerMapping 元件(一)之 AbstractHandlerMapping》**](https://www.cnblogs.com/lifullmoon/p/14137308.html)
- **《HandlerMapping 元件(二)之 HandlerInterceptor 攔截器》**
- **《HandlerMapping 元件(三)之 AbstractHandlerMethodMapping》**
- **《HandlerMapping 元件(四)之 AbstractUrlHandlerMapping》**
## HandlerMapping 元件(一)之 AbstractHandlerMapping
先來回顧一下在 `DispatcherServlet` 中處理請求的過程中哪裡使用到 `HandlerMapping` 元件,可以回到[**《一個請求的旅行過程》**](https://www.cnblogs.com/lifullmoon/p/14131862.html)中的 `DispatcherServlet` 的 `doDispatch` 方法中看看,如下:
```java
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
// ... 省略相關程式碼
// Determine handler for the current request.
// <3> 獲得請求對應的 HandlerExecutionChain 物件(HandlerMethod 和 HandlerInterceptor 攔截器們)
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) { // <3.1> 如果獲取不到,則根據配置丟擲異常或返回 404 錯誤
noHandlerFound(processedRequest, response);
return;
}
// ... 省略相關程式碼
}
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
// 遍歷 handlerMappings 元件們
for (HandlerMapping mapping : this.handlerMappings) {
// 通過 HandlerMapping 元件獲取到 HandlerExecutionChain 物件
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
// 不為空則直接返回
return handler;
}
}
}
return null;
}
```
通過遍歷 HandlerMapping 元件們,根據請求獲取到對應 HandlerExecutionChain 處理器執行鏈。**注意**,這裡是通過一個一個的 HandlerMapping 元件去進行處理,如果找到對應 HandlerExecutionChain 物件則直接返回,不會繼續下去,所以初始化的 HandlerMapping 元件是有一定的先後順序的,預設是BeanNameUrlHandlerMapping -> RequestMappingHandlerMapping
### HandlerMapping 介面
`org.springframework.web.servlet.HandlerMapping` 介面,請求的處理器匹配器,負責為請求找到合適的 `HandlerExecutionChain` 處理器執行鏈,包含處理器(`handler`)和攔截器們(`interceptors`),程式碼如下:
```java
public interface HandlerMapping {
String BEST_MATCHING_HANDLER_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingHandler";
String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping";
String BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern";
String INTROSPECT_TYPE_LEVEL_MAPPING = HandlerMapping.class.getName() + ".introspectTypeLevelMapping";
String URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables";
String MATRIX_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".matrixVariables";
String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes";
/**
* 獲得請求對應的處理器和攔截器們
*/
@Nullable
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}
```
### 類圖
HandlerMapping 介面體系的結構如下:
- 藍色框 **AbstractHandlerMapping** 抽象類,實現了“為請求找到合適的 `HandlerExecutionChain` 處理器執行鏈”對應的的骨架邏輯,而暴露 `getHandlerInternal(HttpServletRequest request)` 抽象方法,交由子類實現。
- AbstractHandlerMapping 的子類,分成兩派,分別是:
- 黃色框 **AbstractUrlHandlerMapping** 系,基於 URL 進行匹配。例如 [《基於 XML 配置的 Spring MVC 簡單的 HelloWorld 例項應用》](https://www.cnblogs.com/liuhongfeng/p/4769076.html) ,當然,目前這種方式已經基本不用了,被 `@RequestMapping` 等註解的方式所取代。不過,Spring MVC 內建的一些路徑匹配,還是使用這種方式。
- 紅色框 **AbstractHandlerMethodMapping** 系,基於 Method 進行匹配。例如,我們所熟知的 `@RequestMapping` 等註解的方式。
- 綠色框的 MatchableHandlerMapping 介面,定義了“判斷請求和指定 `pattern` 路徑是否匹配”的方法。
### 初始化過程
在 `DispatcherServlet` 的 `initHandlerMappings(ApplicationContext context)` 方法,會在 `onRefresh` 方法被呼叫,初始化 HandlerMapping 元件,方法如下:
```java
private void initHandlerMappings(ApplicationContext context) {
// 置空 handlerMappings
this.handlerMappings = null;
// <1> 如果開啟探測功能,則掃描已註冊的 HandlerMapping 的 Bean 們,新增到 handlerMappings 中
if (this.detectAllHandlerMappings) {
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
// 掃描已註冊的 HandlerMapping 的 Bean 們
Map matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context,
HandlerMapping.class, true, false);
// 新增到 handlerMappings 中,並進行排序
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<>(matchingBeans.values());
// We keep HandlerMappings in sorted order.
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
// <2> 如果關閉探測功能,則獲得 Bean 名稱為 "handlerMapping" 對應的 Bean ,將其新增至 handlerMappings
else {
try {
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerMapping later.
}
}
// Ensure we have at least one HandlerMapping, by registering
// a default HandlerMapping if no other mappings are found.
/**
* <3> 如果未獲得到,則獲得預設配置的 HandlerMapping 類
* {@link org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping}
* {@link org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping}
*/
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
if (logger.isTraceEnabled()) {
logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
}
```
1. 如果“開啟”探測功能,則掃描已註冊的 HandlerMapping 的 Bean 們,新增到 `handlerMappings` 中,預設**開啟**
2. 如果“關閉”探測功能,則獲得 Bean 名稱為 "handlerMapping" 對應的 Bean ,將其新增至 `handlerMappings`
3. 如果未獲得到,則獲得預設配置的 HandlerMapping 類,呼叫 `getDefaultStrategies(ApplicationContext context, Class strategyInterface)` 方法,就是從 `DispatcherServlet.properties` 檔案中讀取 HandlerMapping 的預設實現類,如下:
```properties
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
```
可以看到對應的是 BeanNameUrlHandlerMapping 和 RequestMappingHandlerMapping 物件
### AbstractHandlerMapping
`org.springframework.web.servlet.handler.AbstractHandlerMapping`,實現 **HandlerMapping**、Ordered、BeanNameAware 介面,繼承 WebApplicationObjectSupport 抽象類
該類是 HandlerMapping 介面的**抽象基類**,實現了“為請求找到合適的 `HandlerExecutionChain` 處理器執行鏈”對應的的骨架邏輯,而暴露 `getHandlerInternal(HttpServletRequest request)` 抽象方法,交由子類實現
WebApplicationObjectSupport 抽象類,提供 `applicationContext` 屬性的宣告和注入。
#### 構造方法
```java
public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport implements HandlerMapping, Ordered, BeanNameAware {
/**
* 預設處理器
*/
@Nullable
private Object defaultHandler;
/**
* URL 路徑工具類
*/
private UrlPathHelper urlPathHelper = new UrlPathHelper();
/**
* 路徑匹配器
*/
private PathMatcher pathMatcher = new AntPathMatcher();
/**
* 配置的攔截器陣列.
*
* 在 {@link #initInterceptors()} 方法中,初始化到 {@link #adaptedInterceptors} 中
*
* 新增方式有兩種:
* 1. {@link #setInterceptors(Object...)} 方法
* 2. {@link #extendInterceptors(List)} 方法
*/
private final List