該系列文件是本人在學習 Spring MVC 的原始碼過程中總結下來的,可能對讀者不太友好,請結合我的原始碼註釋 Spring MVC 原始碼分析 GitHub 地址 進行閱讀
Spring 版本:5.2.4.RELEASE
該系列其他文件請檢視:《精盡 Spring MVC 原始碼分析 - 文章導讀》
HandlerMapping 元件
HandlerMapping 元件,請求的處理器匹配器,負責為請求找到合適的 HandlerExecutionChain
處理器執行鏈,包含處理器(handler
)和攔截器們(interceptors
)
-
handler
處理器是 Object 型別,可以將其理解成 HandlerMethod 物件(例如我們使用最多的@RequestMapping
註解所標註的方法會解析成該物件),包含了方法的所有資訊,通過該物件能夠執行該方法 -
HandlerInterceptor
攔截器對處理請求進行增強處理,可用於在執行方法前、成功執行方法後、處理完成後進行一些邏輯處理
由於 HandlerMapping 元件涉及到的內容比較多,考慮到內容的排版,所以將這部分內容拆分成了四個模組,依次進行分析:
- 《HandlerMapping 元件(一)之 AbstractHandlerMapping》
- 《HandlerMapping 元件(二)之 HandlerInterceptor 攔截器》
- 《HandlerMapping 元件(三)之 AbstractHandlerMethodMapping》
- 《HandlerMapping 元件(四)之 AbstractUrlHandlerMapping》
HandlerMapping 元件(一)之 AbstractHandlerMapping
先來回顧一下在 DispatcherServlet
中處理請求的過程中哪裡使用到 HandlerMapping
元件,可以回到《一個請求的旅行過程》中的 DispatcherServlet
的 doDispatch
方法中看看,如下:
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
),程式碼如下:
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 例項應用》 ,當然,目前這種方式已經基本不用了,被
@RequestMapping
等註解的方式所取代。不過,Spring MVC 內建的一些路徑匹配,還是使用這種方式。 - 紅色框 AbstractHandlerMethodMapping 系,基於 Method 進行匹配。例如,我們所熟知的
@RequestMapping
等註解的方式。
- 黃色框 AbstractUrlHandlerMapping 系,基於 URL 進行匹配。例如 《基於 XML 配置的 Spring MVC 簡單的 HelloWorld 例項應用》 ,當然,目前這種方式已經基本不用了,被
-
綠色框的 MatchableHandlerMapping 介面,定義了“判斷請求和指定
pattern
路徑是否匹配”的方法。
初始化過程
在 DispatcherServlet
的 initHandlerMappings(ApplicationContext context)
方法,會在 onRefresh
方法被呼叫,初始化 HandlerMapping 元件,方法如下:
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<String, HandlerMapping> 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");
}
}
}
-
如果“開啟”探測功能,則掃描已註冊的 HandlerMapping 的 Bean 們,新增到
handlerMappings
中,預設開啟 -
如果“關閉”探測功能,則獲得 Bean 名稱為 "handlerMapping" 對應的 Bean ,將其新增至
handlerMappings
-
如果未獲得到,則獲得預設配置的 HandlerMapping 類,呼叫
getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface)
方法,就是從DispatcherServlet.properties
檔案中讀取 HandlerMapping 的預設實現類,如下: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
屬性的宣告和注入。
構造方法
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<Object> interceptors = new ArrayList<>();
/**
* 初始化後的攔截器 HandlerInterceptor 陣列
*/
private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<>();
private CorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource();
private CorsProcessor corsProcessor = new DefaultCorsProcessor();
private int order = Ordered.LOWEST_PRECEDENCE; // default: same as non-Ordered
/**
* 當前 Bean 的名稱
*/
@Nullable
private String beanName;
// ... 省略相關 getter、setter 方法
}
-
defaultHandler
:預設處理器,在獲得不到處理器時,可使用該屬性 -
interceptors
:配置的攔截器陣列 -
adaptedInterceptors
:初始化後的攔截器 HandlerInterceptor 陣列,也就是interceptors
轉換成的 HandlerInterceptor 攔截器物件
initApplicationContext
initApplicationContext()
方法,用於初始化攔截器們,方法如下:
在父類 WebApplicationObjectSupport 的父類 ApplicationObjectSupport 中可以看到,因為實現了 ApplicationContextAware 介面,則在初始化該 Bean 的時候會呼叫
setApplicationContext(@Nullable ApplicationContext context)
方法,在這個方法中會呼叫initApplicationContext()
這個方法
@Override
protected void initApplicationContext() throws BeansException {
// <1> 空實現,交給子類實現,用於註冊自定義的攔截器到 interceptors 中,目前暫無子類實現
extendInterceptors(this.interceptors);
// <2> 掃描已註冊的 MappedInterceptor 的 Bean 們,新增到 mappedInterceptors 中
detectMappedInterceptors(this.adaptedInterceptors);
// <3> 將 interceptors 初始化成 HandlerInterceptor 型別,新增到 mappedInterceptors 中
initInterceptors();
}
-
呼叫
extendInterceptors(List<Object> interceptors)
方法,空方法,目前暫無子類實現,暫時忽略 -
呼叫
detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors)
方法,從 Spring 的上下文中,掃描已註冊的 MappedInterceptor 的攔截器們,新增到adaptedInterceptors
中,方法如下:protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) { // 掃描已註冊的 MappedInterceptor 的 Bean 們,新增到 mappedInterceptors 中 // MappedInterceptor 會根據請求路徑做匹配,是否進行攔截 mappedInterceptors.addAll(BeanFactoryUtils .beansOfTypeIncludingAncestors(obtainApplicationContext(), MappedInterceptor.class, true, false) .values()); }
-
呼叫
initInterceptors()
方法,將interceptors
初始化成 HandlerInterceptor 型別,新增到adaptedInterceptors
中,方法如下:protected void initInterceptors() { if (!this.interceptors.isEmpty()) { for (int i = 0; i < this.interceptors.size(); i++) { Object interceptor = this.interceptors.get(i); if (interceptor == null) { throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null"); } // 將 interceptors 初始化成 HandlerInterceptor 型別,新增到 mappedInterceptors 中 // 注意,HandlerInterceptor 無需進行路徑匹配,直接攔截全部 this.adaptedInterceptors.add(adaptInterceptor(interceptor)); } } } protected HandlerInterceptor adaptInterceptor(Object interceptor) { if (interceptor instanceof HandlerInterceptor) { return (HandlerInterceptor) interceptor; } else if (interceptor instanceof WebRequestInterceptor) { return new WebRequestHandlerInterceptorAdapter((WebRequestInterceptor) interceptor); } else { throw new IllegalArgumentException("Interceptor type not supported: " + interceptor.getClass().getName()); } }
關於攔截器在後文進行分析
getHandler
getHandler(HttpServletRequest request)
方法,獲得請求對應的 HandlerExecutionChain
處理器執行鏈,包含處理器(handler
)和攔截器們(interceptors
),方法如下:
@Override
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// <1> 獲得處理器(HandlerMethod 或者 HandlerExecutionChain),該方法是抽象方法,由子類實現
Object handler = getHandlerInternal(request);
// <2> 獲得不到,則使用預設處理器
if (handler == null) {
handler = getDefaultHandler();
}
// <3> 還是獲得不到,則返回 null
if (handler == null) {
return null;
}
// Bean name or resolved handler?
// <4> 如果找到的處理器是 String 型別,則從 Spring 容器中找到對應的 Bean 作為處理器
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
// <5> 建立 HandlerExecutionChain 物件(包含處理器和攔截器)
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (logger.isTraceEnabled()) {
logger.trace("Mapped to " + handler);
}
else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
logger.debug("Mapped to " + executionChain.getHandler());
}
if (CorsUtils.isCorsRequest(request)) {
CorsConfiguration globalConfig = this.corsConfigurationSource.getCorsConfiguration(request);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
-
呼叫
getHandlerInternal(HttpServletRequest request)
抽象方法,獲得handler
處理器 -
如果
handler
處理器沒有找到,則呼叫getDefaultHandler()
方法,使用預設處理器,也就是defaultHandler
屬性 -
如果
handler
處理器沒有找到,且沒有預設的處理器,則直接返回null
-
如果找到的處理器是 String 型別,可能是 Bean 的名稱,則從 Spring 容器中找到對應的 Bean 作為處理器
-
呼叫
getHandlerExecutionChain(Object handler, HttpServletRequest request)
方法,獲得 HandlerExecutionChain 物件,方法如下:protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) { // <1> 建立 HandlerExecutionChain 物件 HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ? (HandlerExecutionChain) handler : new HandlerExecutionChain(handler)); // <2> 獲得請求路徑 String lookupPath = this.urlPathHelper.getLookupPathForRequest(request); // <3> 遍歷 adaptedInterceptors 陣列,獲得請求匹配的攔截器 for (HandlerInterceptor interceptor : this.adaptedInterceptors) { // 需要匹配,若路徑匹配,則新增到 chain 中 if (interceptor instanceof MappedInterceptor) { MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor; if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) { // 匹配 chain.addInterceptor(mappedInterceptor.getInterceptor()); } } // 無需匹配,直接新增到 chain 中 else { chain.addInterceptor(interceptor); } } return chain }
- 建立一個 HandlerExecutionChain 物件,如果
handler
處理器就是該型別物件,則直接使用 - 獲得請求路徑
- 遍歷
adaptedInterceptors
攔截器陣列,根據請求路徑獲得當前請求匹配的攔截器們,新增到 HandlerExecutionChain 物件中
- 建立一個 HandlerExecutionChain 物件,如果
-
返回上面建立的 HandlerExecutionChain 物件
MatchableHandlerMapping
org.springframework.web.servlet.handler.MatchableHandlerMapping
,定義了“判斷請求和指定 pattern
路徑是否匹配”的方法。程式碼如下:
public interface MatchableHandlerMapping extends HandlerMapping {
/**
* 判斷請求和指定 pattern 路徑是否匹配
*/
@Nullable
RequestMatchResult match(HttpServletRequest request, String pattern);
}
RequestMatchResult
org.springframework.web.servlet.handler.RequestMatchResult
類,判斷請求和指定 pattern
路徑是否匹配時,返回的匹配結果,程式碼如下:
public class RequestMatchResult {
/**
* 匹配到的路徑
*/
private final String matchingPattern;
/**
* 被匹配的路徑
*/
private final String lookupPath;
/**
* 路徑匹配器
*/
private final PathMatcher pathMatcher;
public RequestMatchResult(String matchingPattern, String lookupPath, PathMatcher pathMatcher) {
Assert.hasText(matchingPattern, "'matchingPattern' is required");
Assert.hasText(lookupPath, "'lookupPath' is required");
Assert.notNull(pathMatcher, "'pathMatcher' is required");
this.matchingPattern = matchingPattern;
this.lookupPath = lookupPath;
this.pathMatcher = pathMatcher;
}
public Map<String, String> extractUriTemplateVariables() {
return this.pathMatcher.extractUriTemplateVariables(this.matchingPattern, this.lookupPath);
}
}
目前實現 MatchableHandlerMapping 介面的類,有 RequestMappingHandlerMapping 類和 AbstractUrlHandlerMapping 抽象類,在後續都會進行分析
總結
本文對 Spring MVC 處理請求的過程中使用到的 HandlerMapping 元件進行了分析,會為請求找到合適的 HandlerExecutionChain
處理器執行鏈,包含處理器(handler
)和攔截器們(interceptors
)
HandlerMapping 元件的實現類分為兩種:
- 基於 URL 進行匹配。例如 《基於 XML 配置的 Spring MVC 簡單的 HelloWorld 例項應用》 ,當然,目前這種方式已經基本不用了,被
@RequestMapping
等註解的方式所取代。不過,Spring MVC 內建的一些路徑匹配,還是使用這種方式 - 基於 Method 進行匹配。例如,我們所熟知的
@RequestMapping
等註解的方式
AbstractHandlerMapping 抽象類,作為一個基類,實現了“為請求找到合適的 HandlerExecutionChain
處理器執行鏈”對應的的骨架邏輯,而暴露 getHandlerInternal(HttpServletRequest request)
抽象方法,交由子類實現。
本文對 HandlerMapping 元件做了一個簡單的介紹,更多的細節交由其子類去實現,由於涉及到的內容比較多,BeanNameUrlHandlerMapping 和 RequestMappingHandlerMapping 兩種實現類則在後續的文件中依次進行分析
參考文章:芋道原始碼《精盡 Spring MVC 原始碼分析》