參考 知識星球 中 芋道源碼 星球的源碼解析,一個活躍度非常高的 Java 技術社群,感興趣的小伙伴可以加入 芋道源碼 星球,一起學習😄
該系列文檔是本人在學習 Spring MVC 的源碼過程中總結下來的,可能對讀者不太友好,請結合我的源碼注釋 Spring MVC 源碼分析 GitHub 地址 進行閱讀
Spring 版本:5.1.14.RELEASE
該系列其他文檔請查看:《精盡 Spring MVC 源碼分析 - 文章導讀》
HandlerMapping 組件
HandlerMapping 組件,請求的處理器匹配器,負責為請求找到合適的 HandlerExecutionChain
處理器執行鏈,包含處理器(handler
)和攔截器們(interceptors
)
-
handler
處理器是 Object 類型,可以將其理解成 HandlerMethod 對象(例如我們使用最多的@RequestMapping
注解所標注的方法會解析成該對象),包含了方法的所有信息,通過該對象能夠執行該方法 -
HandlerInterceptor
攔截器對處理請求進行增強處理,可用於在執行方法前、成功執行方法后、處理完成后進行一些邏輯處理
由於 HandlerMapping 組件涉及到的內容比較多,考慮到內容的排版,所以將這部分內容拆分成了四個模塊,依次進行分析:
- 《HandlerMapping 組件(一)之 AbstractHandlerMapping》
- 《HandlerMapping 組件(二)之 HandlerInterceptor 攔截器》
- 《HandlerMapping 組件(三)之 AbstractHandlerMethodMapping》
- 《HandlerMapping 組件(四)之 AbstractUrlHandlerMapping》
HandlerMapping 組件(三)之 AbstractHandlerMethodMapping
先來回顧一下HandlerMapping 接口體系的結構:

在《HandlerMapping 組件(一)之 AbstractHandlerMapping》文檔中已經分析了 HandlerMapping 組件的 AbstractHandlerMapping 抽象類基類
那么本文就接着來分析圖中紅色框部分的 AbstractHandlerMethodMapping 系,該系是基於 Method 進行匹配。例如,我們所熟知的 @RequestMapping
等注解的方式。一共就三個類,不多😈😈😈
涉及到的內容比較多,可以直接查看我的總結
回顧
先來回顧一下在 DispatcherServlet
中處理請求的過程中通過 HandlerMapping 組件,獲取到 HandlerExecutionChain 處理器執行鏈的方法,是通過AbstractHandlerMapping 的 getHandler 方法來獲取的,如下:
@Override
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// <1> 獲得處理器(HandlerMethod 或者 HandlerExecutionChain),該方法是抽象方法,由子類實現
Object handler = getHandlerInternal(request);
// <2> 獲得不到,則使用默認處理器
// <3> 還是獲得不到,則返回 null
// <4> 如果找到的處理器是 String 類型,則從 Spring 容器中找到對應的 Bean 作為處理器
// <5> 創建 HandlerExecutionChain 對象(包含處理器和攔截器)
// ... 省略相關代碼
return executionChain;
}
在 AbstractHandlerMapping 獲取 HandlerExecutionChain 處理器執行鏈的方法中,需要先調用 getHandlerInternal(HttpServletRequest request)
抽象方法,獲取請求對應的處理器,該方法由子類去實現,也就上圖中黃色框和紅色框兩類子類,本文分析紅色框部分內容
注解
Spring MVC 的請求匹配的注解,體系結構如下:

關於這些注解,大家已經非常熟悉了,各自的屬性就不再進行講述了,可具體查看源碼:
AbstractHandlerMethodMapping
org.springframework.web.servlet.result.method.AbstractHandlerMethodMapping
,實現 InitializingBean 接口,繼承 AbstractHandlerMapping 抽象類,以 Method 方法 作為 Handler 處理器 的 HandlerMapping 抽象類,提供 Mapping 的初始化、注冊等通用的骨架方法。
那么具體是什么呢?AbstractHandlerMethodMapping 定義為了 <T>
泛型,交給子類做決定。例如,子類 RequestMappingInfoHandlerMapping 使用 RequestMappingInfo 類作為 <T>
泛型,也就是我們在上面注解模塊看到的 @RequestMapping
等注解信息。
構造方法
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
/**
* 是否只掃描可訪問的 HandlerMethod 們
*/
private boolean detectHandlerMethodsInAncestorContexts = false;
/**
* Mapping 命名策略
*/
@Nullable
private HandlerMethodMappingNamingStrategy<T> namingStrategy;
/**
* Mapping 注冊表
*/
private final MappingRegistry mappingRegistry = new MappingRegistry();
}
-
<T>
泛型,就是我們前面要一直提到的 Mapping 類型 -
mappingRegistry
:Mapping 注冊表,詳細見下文 -
namingStrategy
:org.springframework.web.servlet.handler.HandlerMethodMappingNamingStrategy
接口,HandlerMethod 的 Mapping 的名字生成策略接口@FunctionalInterface public interface HandlerMethodMappingNamingStrategy<T> { /** * 根據 HandlerMethod 獲取名稱,就是為對應的 Mappring 對象生成一個名稱,便於獲取 */ String getName(HandlerMethod handlerMethod, T mapping); } // org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMethodMappingNamingStrateg.java public class RequestMappingInfoHandlerMethodMappingNamingStrategy implements HandlerMethodMappingNamingStrategy<RequestMappingInfo> { /** Separator between the type and method-level parts of a HandlerMethod mapping name. */ public static final String SEPARATOR = "#"; @Override public String getName(HandlerMethod handlerMethod, RequestMappingInfo mapping) { // 情況一,mapping 名字非空,則使用 mapping 的名字 if (mapping.getName() != null) { return mapping.getName(); } // 情況二,使用類名大寫 + "#" + 方法名 StringBuilder sb = new StringBuilder(); String simpleTypeName = handlerMethod.getBeanType().getSimpleName(); for (int i = 0; i < simpleTypeName.length(); i++) { if (Character.isUpperCase(simpleTypeName.charAt(i))) { sb.append(simpleTypeName.charAt(i)); } } sb.append(SEPARATOR).append(handlerMethod.getMethod().getName()); return sb.toString(); } }
- 情況一,如果 Mapping 已經配置名字,則直接返回。例如,
@RequestMapping(name = "login", value = "user/login")
注解的方法,它對應的 Mapping 的名字就是login
- 情況二,如果 Mapping 未配置名字,則使用使用類名大寫 +
"#"
+ 方法名。例如,@RequestMapping(value = "user/login")
注解的方法,假設它所在的類為 UserController ,對應的方法名為 login ,則它對應的 Mapping 的名字就是USERCONTROLLER#login
- 情況一,如果 Mapping 已經配置名字,則直接返回。例如,
MappingRegistry 注冊表
AbstractHandlerMethodMapping 的內部類,Mapping 注冊表
構造方法
class MappingRegistry {
/**
* 注冊表
*
* Key: Mapping
* Value:{@link MappingRegistration}(Mapping + HandlerMethod)
*/
private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
/**
* 注冊表2
*
* Key:Mapping
* Value:{@link HandlerMethod}
*/
private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();
/**
* 直接 URL 的映射
*
* Key:直接 URL(就是固定死的路徑,而非多個)
* Value:Mapping 數組
*/
private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();
/**
* Mapping 的名字與 HandlerMethod 的映射
*
* Key:Mapping 的名字
* Value:HandlerMethod 數組
*/
private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();
private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();
/**
* 讀寫鎖
*/
private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
}
registry
:注冊表。Key: Mapping,即<T>
泛型;Value:MappingRegistration 對象(Mapping + HandlerMethod)mappingLookup
:注冊表2。Key: Mapping,即<T>
泛型;Value:HandlerMethod 對象urlLookup
:直接 URL 的映射。Key:直接 URL(就是固定死的路徑,而非多個);Value:Mapping 數組nameLookup
:Mapping 的名字與 HandlerMethod 的映射。Key:Mapping 的名字;Value:HandlerMethod 數組readWriteLock
:讀寫鎖,為了才操作上述屬性時保證線程安全
register
register(T mapping, Object handler, Method method)
方法,將 Mapping、Method、handler(方法所在類)之間的映射關系進行注冊,會生成 HandlerMethod 對象,就是處理器對象,方法如下:
public void register(T mapping, Object handler, Method method) {
// <1> 獲得寫鎖
this.readWriteLock.writeLock().lock();
try {
// <2.1> 創建 HandlerMethod 對象
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
// <2.2> 校驗當前 mapping 是否存在對應的 HandlerMethod 對象,如果已存在但不是當前的 handlerMethod 對象則拋出異常
assertUniqueMethodMapping(handlerMethod, mapping);
// <2.3> 將 mapping 與 handlerMethod 的映射關系保存至 this.mappingLookup
this.mappingLookup.put(mapping, handlerMethod);
// <3.1> 獲得 mapping 對應的普通 URL 數組
List<String> directUrls = getDirectUrls(mapping);
// <3.2> 將 url 和 mapping 的映射關系保存至 this.urlLookup
for (String url : directUrls) {
this.urlLookup.add(url, mapping);
}
// <4> 初始化 nameLookup
String name = null;
if (getNamingStrategy() != null) {
// <4.1> 獲得 Mapping 的名字
name = getNamingStrategy().getName(handlerMethod, mapping);
// <4.2> 將 mapping 的名字與 HandlerMethod 的映射關系保存至 this.nameLookup
addMappingName(name, handlerMethod);
}
// <5> 初始化 CorsConfiguration 配置對象
CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
if (corsConfig != null) {
this.corsLookup.put(handlerMethod, corsConfig);
}
// <6> 創建 MappingRegistration 對象
// 並與 mapping 映射添加到 registry 注冊表中
this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
}
finally {
// <7> 釋放寫鎖
this.readWriteLock.writeLock().unlock();
}
}
-
獲得寫鎖
-
添加相關映射至
Map<T, HandlerMethod> mappingLookup
屬性- 調用
createHandlerMethod(Object handler, Method method)
方法,創建 HandlerMethod 對象,詳情見下文 - 校驗當前 Mapping 是否存在對應的 HandlerMethod 對象,如果已存在但不是前一步創建 HandlerMethod 對象則拋出異常,保證唯一性
- 將 Mapping 與 HandlerMethod 的映射關系保存至
mappingLookup
- 調用
-
添加相關映射至
MultiValueMap<String, T> urlLookup
屬性-
調用
getDirectUrls
方法,獲得 Mapping 對應的直接 URL 數組,如下:private List<String> getDirectUrls(T mapping) { List<String> urls = new ArrayList<>(1); // 遍歷 Mapping 對應的路徑 for (String path : getMappingPathPatterns(mapping)) { // 非**模式**路徑 if (!getPathMatcher().isPattern(path)) { urls.add(path); } } return urls; }
- 例如,
@RequestMapping("/user/login")
注解對應的路徑,就是直接路徑 - 例如,
@RequestMapping("/user/${id}")
注解對應的路徑,不是直接路徑,因為不確定性
- 例如,
-
將 url 和 Mapping 的映射關系保存至
urlLookup
-
-
添加相關映射至
Map<String, List<HandlerMethod>> nameLookup
屬性-
調用
HandlerMethodMappingNamingStrategy#getName(HandlerMethod handlerMethod, T mapping)
方法,獲得 Mapping 的名字 -
調用
addMappingName(String name, HandlerMethod handlerMethod)
方法,添加Mapping
的名字 + HandlerMethod 至nameLookup
,如下:private void addMappingName(String name, HandlerMethod handlerMethod) { // 獲得 Mapping 的名字,對應的 HandlerMethod 數組 List<HandlerMethod> oldList = this.nameLookup.get(name); if (oldList == null) { oldList = Collections.emptyList(); } // 如果已經存在,則不用添加 for (HandlerMethod current : oldList) { if (handlerMethod.equals(current)) { return; } } // 添加到 nameLookup 中 List<HandlerMethod> newList = new ArrayList<>(oldList.size() + 1); newList.addAll(oldList); newList.add(handlerMethod); this.nameLookup.put(name, newList); }
和已有的進行合並,說明名稱不是唯一哦
-
-
初始化 CorsConfiguration 配置對象,暫時忽略
-
創建
MappingRegistration
對象,並和 Mapping 進行映射添加至registry
-
釋放寫鎖
unregister
unregister(T mapping)
方法,取消上面方法注冊的相關信息,方法如下:
public void unregister(T mapping) {
// 獲得寫鎖
this.readWriteLock.writeLock().lock();
try {
// 從 registry 中移除
MappingRegistration<T> definition = this.registry.remove(mapping);
if (definition == null) {
return;
}
// 從 mappingLookup 中移除
this.mappingLookup.remove(definition.getMapping());
// 從 urlLookup 移除
for (String url : definition.getDirectUrls()) {
List<T> list = this.urlLookup.get(url);
if (list != null) {
list.remove(definition.getMapping());
if (list.isEmpty()) {
this.urlLookup.remove(url);
}
}
}
// 從 nameLookup 移除
removeMappingName(definition);
// 從 corsLookup 中移除
this.corsLookup.remove(definition.getHandlerMethod());
}
finally {
// 釋放寫鎖
this.readWriteLock.writeLock().unlock();
}
和 register
方法邏輯相反,依次移除相關映射
createHandlerMethod
createHandlerMethod(Object handler, Method method)
方法,創建 Method 對應的 HandlerMethod 對象
protected HandlerMethod createHandlerMethod(Object handler, Method method) {
HandlerMethod handlerMethod;
// <1> 如果 handler 類型為 String, 說明對應一個 Bean 對象的名稱
// 例如 UserController 使用 @Controller 注解后,默認入參 handler 就是它的 beanName ,即 `userController`
if (handler instanceof String) {
String beanName = (String) handler;
handlerMethod = new HandlerMethod(beanName, obtainApplicationContext().getAutowireCapableBeanFactory(), method);
}
// <2> 如果 handler 類型非 String ,說明是一個已經是一個 handler 對象,就無需處理,直接創建 HandlerMethod 對象
else {
handlerMethod = new HandlerMethod(handler, method);
}
return handlerMethod;
}
- 如果 handler 類型為 String, 說明對應一個 Bean 對象的名稱。例如 UserController 使用
@Controller
注解后,默認入參 handler 就是它的 beanName ,即userController
- 如果 handler 類型非 String ,說明是一個已經是一個 handler 對象,就無需處理,直接創建 HandlerMethod 對象
所以你會發現 HandlerMethod 處理器對象,就是handler(方法所在類)
+method(方法對象)
的組合,通過它能執行該方法
HandlerMethod 處理器
org.springframework.web.method.HandlerMethod
,處理器對象,也就是某個方法的封裝對象(Method+所在類的 Bean 對象),有以下屬性:
public class HandlerMethod {
/**
* Bean 對象
*/
private final Object bean;
@Nullable
private final BeanFactory beanFactory;
/**
* Bean 的類型
*/
private final Class<?> beanType;
/**
* 方法對象
*/
private final Method method;
/**
* {@link #method} 的橋接方法
* 存在泛型類型,編譯器則會自動生成一個橋接方法(java1.5向后兼容)
*/
private final Method bridgedMethod;
/**
* 方法的參數類型數組
*/
private final MethodParameter[] parameters;
/**
* 響應的狀態碼,即 {@link ResponseStatus#code()}
*/
@Nullable
private HttpStatus responseStatus;
/**
* 響應的狀態碼原因,即 {@link ResponseStatus#reason()}
*/
@Nullable
private String responseStatusReason;
/**
* 解析自哪個 HandlerMethod 對象
*
* 僅構造方法中傳入 HandlerMethod 類型的參數適用,例如 {@link #HandlerMethod(HandlerMethod)}
*/
@Nullable
private HandlerMethod resolvedFromHandlerMethod;
/**
* 父接口的方法的參數注解數組
*/
@Nullable
private volatile List<Annotation[][]> interfaceParameterAnnotations;
}
根據上面的注釋理解上面的屬性,包含該方法的所有信息
它的構造函數非常多,不過原理都差不多,我們挑兩個來看看
HandlerMethod(String beanName, BeanFactory beanFactory, Method method) 構造方法
對應 createHandlerMethod(Object handler, Method method)
方法的 <1>
,代碼如下:
public HandlerMethod(String beanName, BeanFactory beanFactory, Method method) {
Assert.hasText(beanName, "Bean name is required");
Assert.notNull(beanFactory, "BeanFactory is required");
Assert.notNull(method, "Method is required");
// <1> 將 beanName 賦值給 bean 屬性,說明 beanFactory + bean 的方式,獲得 handler 對象
this.bean = beanName;
this.beanFactory = beanFactory;
// <2> 初始化 beanType 屬性
Class<?> beanType = beanFactory.getType(beanName);
if (beanType == null) {
throw new IllegalStateException("Cannot resolve bean type for bean with name '" + beanName + "'");
}
this.beanType = ClassUtils.getUserClass(beanType);
// <3> 初始化 method、bridgedMethod 屬性
this.method = method;
// 如果不是橋接方法則直接為該方法
this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
// <4> 初始化 parameters 屬性,解析該方法(或者橋接方法)的參數類型
this.parameters = initMethodParameters();
// <5> 初始化 responseStatus、responseStatusReason 屬性,通過 @ResponseStatus 注解
evaluateResponseStatus();
}
-
將
beanName
賦值給bean
屬性,說明beanFactory + bean
的方式,獲得handler
對象 -
初始化
beanType
屬性 -
初始化
method
、bridgedMethod
屬性 -
初始化
parameters
屬性,解析該方法(或者橋接方法)的參數類型,調用initMethodParameters()
方法,如下:private MethodParameter[] initMethodParameters() { int count = this.bridgedMethod.getParameterCount(); // 創建 MethodParameter 數組 MethodParameter[] result = new MethodParameter[count]; // 遍歷 bridgedMethod 方法的參數,逐個解析它的參數類型 for (int i = 0; i < count; i++) { HandlerMethodParameter parameter = new HandlerMethodParameter(i); GenericTypeResolver.resolveParameterType(parameter, this.beanType); result[i] = parameter; } return result; }
-
初始化
responseStatus
、responseStatusReason
屬性,通過@ResponseStatus
注解
關於橋接方法呢,是 java1.5 引入泛型向后兼容的一種方法,具體可參考Effects of Type Erasure and Bridge Methods
HandlerMethod(Object bean, Method method) 構造方法
對應 createHandlerMethod(Object handler, Method method)
方法的 <2>
,代碼如下:
public HandlerMethod(Object bean, Method method) {
Assert.notNull(bean, "Bean is required");
Assert.notNull(method, "Method is required");
// <1> 初始化 Bean
this.bean = bean;
this.beanFactory = null;
// <2> 初始化 beanType 屬性
this.beanType = ClassUtils.getUserClass(bean);
// <3> 初始化 method、bridgedMethod 屬性
this.method = method;
// 如果不是橋接方法則直接為該方法
this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
// <4> 初始化 parameters 屬性,解析該方法(或者橋接方法)的參數類型
this.parameters = initMethodParameters();
// <5> 初始化 responseStatus、responseStatusReason 屬性,通過 @ResponseStatus 注解
evaluateResponseStatus();
}
和上面的構造方法差不多,不同的是這里的 bean
對象就是方法所在類的 Bean 對象
MappingRegistration 注冊登記
AbstractHandlerMethodMapping 的私有靜態內部類,Mapping 的注冊登記信息,包含 Mapiing、HandlerMethod、直接 URL 路徑、Mapping 名稱,代碼如下:
private static class MappingRegistration<T> {
/**
* Mapping 對象
*/
private final T mapping;
/**
* HandlerMethod 對象
*/
private final HandlerMethod handlerMethod;
/**
* 直接 URL 數組(就是固定死的路徑,而非多個)
*/
private final List<String> directUrls;
/**
* {@link #mapping} 的名字
*/
@Nullable
private final String mappingName;
public MappingRegistration(T mapping, HandlerMethod handlerMethod,
@Nullable List<String> directUrls, @Nullable String mappingName) {
Assert.notNull(mapping, "Mapping must not be null");
Assert.notNull(handlerMethod, "HandlerMethod must not be null");
this.mapping = mapping;
this.handlerMethod = handlerMethod;
this.directUrls = (directUrls != null ? directUrls : Collections.emptyList());
this.mappingName = mappingName;
}
}
很簡單,就是保存了 Mapping 注冊時的一些信息
1.afterPropertiesSet 初始化方法
因為 AbstractHandlerMethodMapping 實現了 InitializingBean 接口,在 Sping 初始化該 Bean 的時候,會調用該方法,完成一些初始化工作,方法如下:
@Override
public void afterPropertiesSet() {
// <x> 初始化處理器的方法們
initHandlerMethods();
}
protected void initHandlerMethods() {
// <1> 遍歷 Bean ,逐個處理
for (String beanName : getCandidateBeanNames()) {
// 排除目標代理類,AOP 相關,可查看注釋
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
// <2> 處理 Bean
processCandidateBean(beanName);
}
}
// <3> 初始化處理器的方法們,目前是空方法,暫無具體的實現
handlerMethodsInitialized(getHandlerMethods());
}
-
調用
getCandidateBeanNames()
方法,獲取到 Spring 上下文中所有為Object
類型的 Bean 的名稱集合,然后進行遍歷,方法如下:protected String[] getCandidateBeanNames() { // 獲取上下文中所有的 Bean 的名稱 return (this.detectHandlerMethodsInAncestorContexts ? BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) : obtainApplicationContext().getBeanNamesForType(Object.class)); }
detectHandlerMethodsInAncestorContexts
:是否只掃描可訪問的 HandlerMethod 們,默認false
-
調用
processCandidateBean(String beanName)
方法,處理每個符合條件的 Bean 對象(例如有@Controller
或者@RequestMapping
注解的 Bean),解析這些 Bean 下面相應的方法,往MappingRegistry
注冊,詳情見下文 -
調用
handlerMethodsInitialized(Map<T, HandlerMethod> handlerMethods)
方法,初始化 HandlerMethod 處理器們,空方法,暫無子類實現
2.processCandidateBean
processCandidateBean(String beanName)
方法,處理符合條件的 Bean 對象,解析其相應的方法,往 MappingRegistry
注冊,方法如下:
protected void processCandidateBean(String beanName) {
// <1> 獲得 Bean 對應的 Class 對象
Class<?> beanType = null;
try {
beanType = obtainApplicationContext().getType(beanName);
}
catch (Throwable ex) {
// An unresolvable bean type, probably from a lazy bean - let's ignore it.
if (logger.isTraceEnabled()) {
logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
}
}
// <2> 判斷 Bean 是否為處理器(例如有 @Controller 或者 @RequestMapping 注解)
if (beanType != null && isHandler(beanType)) {
// <3> 掃描處理器方法
detectHandlerMethods(beanName);
}
}
- 獲得 Bean 對應的 Class 對象
- 調用
isHandler(Class<?> beanType)
抽象方法,判斷 Bean 的類型是否需要處理,其RequestMappingHandlerMapping
子類的實現:有@Controller
或者@RequestMapping
注解的 Bean - 調用
detectHandlerMethods(Object handler)
方法,掃描 Bean 的方法進行處理
3.detectHandlerMethods
detectHandlerMethods(Object handler)
方法,初始化 Bean 下面的方法們為 HandlerMethod 對象,並注冊到 MappingRegistry 注冊表中,代碼如下:
protected void detectHandlerMethods(Object handler) {
// <1> 獲得 Bean 對應的 Class 對象
Class<?> handlerType = (handler instanceof String ? obtainApplicationContext().getType((String) handler) : handler.getClass());
if (handlerType != null) {
// <2> 獲得真實的 Class 對象,因為 `handlerType` 可能是代理類
Class<?> userType = ClassUtils.getUserClass(handlerType);
// <3> 獲得匹配的方法和對應的 Mapping 對象
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
(MethodIntrospector.MetadataLookup<T>) method -> {
try {
// 創建該方法對應的 Mapping 對象,例如根據 @RequestMapping 注解創建 RequestMappingInfo 對象
return getMappingForMethod(method, userType);
} catch (Throwable ex) {
throw new IllegalStateException(
"Invalid mapping on handler class [" + userType.getName() + "]: " + method, ex);
}
});
if (logger.isTraceEnabled()) {
logger.trace(formatMappings(userType, methods));
}
// <4> 遍歷方法,逐個注冊 HandlerMethod
methods.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
-
獲得 Bean 對應的 Class 對象
handlerType
-
調用
getUserClass(Class<?> clazz)
方法,獲得真實的 Class 對象,因為handlerType
可能是代理類,如下:public static Class<?> getUserClass(Class<?> clazz) { // 如果 Class 對象的名稱包含 "$$",則是 CG_CLASS 代理類,則獲取其父類 if (clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) { Class<?> superclass = clazz.getSuperclass(); if (superclass != null && superclass != Object.class) { return superclass; } } return clazz; }
-
獲得匹配的方法和對應的 Mapping 對象,其中泛型
T
,也就是 Mapping 對象,需要通過getMappingForMethod(Method method, Class<?> handlerType)
抽象方法返回,其RequestMappingHandlerMapping
子類的實現,根據@RequestMapping
注解為方法創建RequestMappingInfo
對象所以這里的 Mapping 對象就是
RequestMappingInfo
對象 -
遍歷方法(方法與 Mapping 一一映射了),調用
registerHandlerMethod(Object handler, Method method, T mapping)
,逐個注冊對應的 HandlerMethod 對象,如下:protected void registerHandlerMethod(Object handler, Method method, T mapping) { this.mappingRegistry.register(mapping, handler, method); }
也就是上面 MappingRegistry 的 register 方法,已經分析過了😄
到這里我們已經分析完了 AbstractHandlerMethodMapping 的初始化工作,部分細節在子類中實現
大致邏輯:掃描有
@Controller
或者@RequestMapping
注解的類下面的方法,如果方法上面有@RequestMapping
注解,則會為該方法創建對應的RequestMappingInfo
對象將所有的
RequestMappingInfo
對象和 Method 以及方法所在類,往 MappingRegistry 進行注冊,會生成 HandlerMethod 處理器(Method 所有信息)對象這樣一來,當 Spring MVC 的 DispatcherServlet 處理請求的時候,獲取到對應的 HandlerMethod 處理器,就可以通過反射執行對應的方法了
到這里,思路是不是越來越清晰了,我們繼續往下分析
【重點】getHandlerInternal
由於上面初始化涉及到內容有點多,先回到本文上面的回顧這一小節,通過 AbstractHandlerMapping 的
getHandler(HttpServletRequest request)
方法獲取 HandlerExecutionChain 處理器執行鏈時,需要調用getHandlerInternal
抽象方法獲取處理器,這個方法由子類去實現,就到這里了
getHandlerInternal(ServerWebExchange exchange)
方法,獲得請求對應的 HandlerMethod 處理器對象,方法如下:
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
// <1> 獲得請求的路徑
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
// <2> 獲得讀鎖
this.mappingRegistry.acquireReadLock();
try {
// <3> 獲得 HandlerMethod 對象
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
// <4> 進一步,獲得一個新的 HandlerMethod 對象
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
// <5> 釋放讀鎖
this.mappingRegistry.releaseReadLock();
}
}
-
獲得請求路徑
-
獲得讀鎖
-
調用
lookupHandlerMethod(ServerWebExchange exchange)
方法,獲得請求對應的 HandlerMethod 處理器對象,詳情見下文 -
如果獲得到 HandlerMethod 對象,則調用
HandlerMethod#createWithResolvedBean()
方法,進一步,獲得 HandlerMethod 對象,如下:public HandlerMethod createWithResolvedBean() { Object handler = this.bean; // 如果是 bean 是 String 類型,則獲取對應的 Bean,因為創建該對象時 bean 可能是對應的 beanName if (this.bean instanceof String) { Assert.state(this.beanFactory != null, "Cannot resolve bean name without BeanFactory"); String beanName = (String) this.bean; handler = this.beanFactory.getBean(beanName); } return new HandlerMethod(this, handler); }
-
釋放讀鎖
lookupHandlerMethod 獲取處理器方法
lookupHandlerMethod(ServerWebExchange exchange)
方法,獲得請求對應的 HandlerMethod 處理器對象,方法如下:
@Nullable
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
// <1> Match 數組,存儲匹配上當前請求的結果(Mapping + HandlerMethod)
List<Match> matches = new ArrayList<>();
// <1.1> 優先,基於直接 URL (就是固定死的路徑,而非多個)的 Mapping 們,進行匹配
List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
if (directPathMatches != null) {
addMatchingMappings(directPathMatches, matches, request);
}
// <1.2> 其次,掃描注冊表的 Mapping 們,進行匹配
if (matches.isEmpty()) {
// No choice but to go through all mappings...
addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
}
// <2> 如果匹配到,則獲取最佳匹配的 Match 結果的 `HandlerMethod`屬性
if (!matches.isEmpty()) {
// <2.1> 創建 MatchComparator 對象,排序 matches 結果,排序器
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
matches.sort(comparator);
// <2.2> 獲得首個 Match 對象,也就是最匹配的
Match bestMatch = matches.get(0);
// <2.3> 處理存在多個 Match 對象的情況!!
if (matches.size() > 1) {
if (logger.isTraceEnabled()) {
logger.trace(matches.size() + " matching mappings: " + matches);
}
if (CorsUtils.isPreFlightRequest(request)) {
return PREFLIGHT_AMBIGUOUS_MATCH;
}
// 比較 bestMatch 和 secondBestMatch ,如果相等,說明有問題,拋出 IllegalStateException 異常
// 因為,兩個優先級一樣高,說明無法判斷誰更優先
Match secondBestMatch = matches.get(1);
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
Method m1 = bestMatch.handlerMethod.getMethod();
Method m2 = secondBestMatch.handlerMethod.getMethod();
String uri = request.getRequestURI();
throw new IllegalStateException(
"Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
}
}
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
// <2.4> 處理首個 Match 對象
handleMatch(bestMatch.mapping, lookupPath, request);
// <2.5> 返回首個 Match 對象的 handlerMethod 屬性
return bestMatch.handlerMethod;
}
// <3> 如果匹配不到,則處理不匹配的情況
else {
return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
}
}
-
定義 Match 數組
matches
,存儲匹配上當前請求的結果(Mapping
+HandlerMethod
)- 優先,基於直接 URL (就是固定死的路徑,而非多個)的
Mapping
們,進行匹配 - 其次,掃描注冊表的 Mapping 們,進行匹配
上述的
1.1
和1.2
,都會調用addMatchingMappings(Collection<T> mappings, List<Match> matches, ServerWebExchange exchange)
方法將當前請求和注冊表中的 Mapping 進行匹配,匹配成功則生成匹配結果 Match,添加到
matches
中,方法如下:private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) { // 遍歷 Mapping 數組 for (T mapping : mappings) { // <1> 執行匹配,抽象方法,交由子類實現 T match = getMatchingMapping(mapping, request); if (match != null) { // <2> 如果匹配,則創建 Match 對象,添加到 matches 中 matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping))); } } }
- 優先,基於直接 URL (就是固定死的路徑,而非多個)的
-
如果匹配到,則獲取最佳匹配的 Match 結果的
HandlerMethod
屬性- 創建 MatchComparator 對象,排序
matches
的結果,排序器 - 獲得首個 Match 結果,也就是最匹配的
- 處理存在多個 Match 的情況,則判斷第二匹配的和最匹配的是否“相同”,是的話就拋出異常
- 處理最匹配的 Match,設置請求路徑
lookupPath
到請求屬性 - 返回最匹配的 Match 的
HandlerMethod
處理器對象
- 創建 MatchComparator 對象,排序
-
如果匹配不到,則處理不匹配的情況,調用
handleNoMatch(Set<T> mappings, String lookupPath, HttpServletRequest request)
方法,這里返回null
到這里 AbstractHandlerMethodMapping 抽象類差不多全部分析完了,其中有幾個抽象方法交由子類去實現
protected abstract boolean isHandler(Class<?> beanType);
protected abstract T getMappingForMethod(Method method, Class<?> handlerType);
protected abstract Set<String> getMappingPathPatterns(T mapping);
protected abstract T getMatchingMapping(T mapping, HttpServletRequest request);
protected abstract Comparator<T> getMappingComparator(HttpServletRequest request);
RequestMappingInfoHandlerMapping
org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping
,繼承 AbstractHandlerMethodMapping 抽象類,定義了使用的泛型 <T>
為 org.springframework.web.servlet.mvc.method.RequestMappingInfo
類,即 Mapping 類型就是 RequestMappingInfo 對象
這樣有什么好處呢?
RequestMappingInfoHandlerMapping
定義了使用 RequestMappingInfo
對象,而其子類 RequestMappingHandlerMapping
將使用了 @RequestMapping
注解的方法,解析生成 RequestMappingInfo
對象。這樣,如果未來我們自己定義注解,或者其他方式來生成 RequestMappingHandlerMapping
對象,未嘗不可。
構造方法
public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMethodMapping<RequestMappingInfo> {
protected RequestMappingInfoHandlerMapping() {
// 設置父類的 namingStrategy 屬性 Mapping 命名策略對象,為 RequestMappingInfoHandlerMethodMappingNamingStrategy 對象
setHandlerMethodMappingNamingStrategy(new RequestMappingInfoHandlerMethodMappingNamingStrategy());
}
}
-
<T>
泛型,為RequestMappingInfo
類型 -
設置父類
AbstractHandlerMethodMapping
的namingStrategy
屬性為RequestMappingInfoHandlerMethodMappingNamingStrategy
對象是否還記得這個為 Mapping 生成名稱的類?在 AbstractHandlerMethodMapping 中進行分析過了
RequestMappingInfo 對象
RequestMappingInfo 不是 RequestMappingInfoHandlerMapping 的內部類,而是 RequestMappingInfoHandlerMapping 的前綴
org.springframework.web.servlet.mvc.method.RequestMappingInfo
,實現 RequestCondition 接口,每個方法的定義的請求信息,也就是 @RequestMapping
等注解的信息
關於
org.springframework.web.servlet.mvc.condition.RequestCondition
,條件接口,定義了三個方法,分別是:
combine(T other)
,合並方法getMatchingCondition(HttpServletRequest request)
,匹配方法compareTo(T other, HttpServletRequest request)
,比較方法
構造方法
public final class RequestMappingInfo implements RequestCondition<RequestMappingInfo> {
/**
* 名字
*/
@Nullable
private final String name;
/**
* 請求路徑的條件
*/
private final PatternsRequestCondition patternsCondition;
/**
* 請求方法的條件
*/
private final RequestMethodsRequestCondition methodsCondition;
/**
* 請求參數的條件
*/
private final ParamsRequestCondition paramsCondition;
/**
* 請求頭的條件
*/
private final HeadersRequestCondition headersCondition;
/**
* 可消費的 Content-Type 的條件
*/
private final ConsumesRequestCondition consumesCondition;
/**
* 可生產的 Content-Type 的條件
*/
private final ProducesRequestCondition producesCondition;
/**
* 自定義的條件
*/
private final RequestConditionHolder customConditionHolder;
}
- 可以看到屬性中有各種條件。實際上,和
@RequestMapping
注解是一一對應的。所以,每個屬性的詳細解釋,相信你經常使用到 - 😈 實際上,我們日常使用最多的還是
patternsCondition
請求路徑條件,和methodsCondition
請求方法條件
RequestCondition 接口體系結構如下:

getMatchingCondition
getMatchingCondition(HttpServletRequest request)
方法,從當前 RequestMappingInfo 獲得匹配的條件。如果匹配,則基於其匹配的條件,創建新的 RequestMappingInfo 對象,如果不匹配,則返回 null
,代碼如下:
@Override
@Nullable
public RequestMappingInfo getMatchingCondition(HttpServletRequest request) {
// 匹配 methodsCondition、paramsCondition、headersCondition、consumesCondition、producesCondition
// 如果任一為空,則返回 null ,表示匹配失敗
RequestMethodsRequestCondition methods = this.methodsCondition.getMatchingCondition(request);
if (methods == null) {
return null;
}
ParamsRequestCondition params = this.paramsCondition.getMatchingCondition(request);
if (params == null) {
return null;
}
HeadersRequestCondition headers = this.headersCondition.getMatchingCondition(request);
if (headers == null) {
return null;
}
ConsumesRequestCondition consumes = this.consumesCondition.getMatchingCondition(request);
if (consumes == null) {
return null;
}
ProducesRequestCondition produces = this.producesCondition.getMatchingCondition(request);
if (produces == null) {
return null;
}
PatternsRequestCondition patterns = this.patternsCondition.getMatchingCondition(request);
if (patterns == null) {
return null;
}
RequestConditionHolder custom = this.customConditionHolder.getMatchingCondition(request);
if (custom == null) {
return null;
}
/*
* 創建匹配的 RequestMappingInfo 對象
* 為什么要創建 RequestMappingInfo 對象呢?
*
* 因為當前 RequestMappingInfo 對象,一個 methodsCondition 可以配置 GET、POST、DELETE 等等條件,
* 但是實際就匹配一個請求類型,此時 methods 只代表其匹配的那個。
*/
return new RequestMappingInfo(this.name, patterns,
methods, params, headers, consumes, produces, custom.getCondition());
}
- 雖然代碼非常長,實際都是調用每個屬性對應的
getMatchingCondition(HttpServletRequest request)
方法,獲得其匹配的真正的條件
可能你會疑惑,如果一個 @RequestMapping(value = "user/login")
注解,並未寫 RequestMethod 的條件,豈不是會報空?
實際上不會。在這種情況下,會創建一個 RequestMethodsRequestCondition 對象,並且在匹配時,直接返回自身,代碼如下:
@Override
@Nullable
public RequestMethodsRequestCondition getMatchingCondition(HttpServletRequest request) {
if (CorsUtils.isPreFlightRequest(request)) {
return matchPreFlight(request);
}
// 空的情況下,就返回自身
if (getMethods().isEmpty()) {
if (RequestMethod.OPTIONS.name().equals(request.getMethod()) &&
!DispatcherType.ERROR.equals(request.getDispatcherType())) {
return null; // No implicit match for OPTIONS (we handle it)
}
return this;
}
// 非空,逐個匹配
return matchRequestMethod(request.getMethod());
}
也就是說,沒有 RequestMethod 的條件,則一定匹配成功,且結果就是自身 RequestMethodsRequestCondition 對象
總結:就是根據配置的 @RequestMapping
注解,如果所有條件都滿足,則創建一個 RequestMappingInfo 對象返回,如果某個條件不滿足則直接返回 null
,表示不匹配
compareTo
compareTo(RequestMappingInfo other, HttpServletRequest request)
方法,比較優先級,方法如下:
@Override
public int compareTo(RequestMappingInfo other, HttpServletRequest request) {
int result;
// Automatic vs explicit HTTP HEAD mapping
// 針對 HEAD 請求方法,特殊處理
if (HttpMethod.HEAD.matches(request.getMethod())) {
result = this.methodsCondition.compareTo(other.getMethodsCondition(), request);
if (result != 0) {
return result;
}
}
/*
* 依次比較 patternsCondition、paramsCondition、headersCondition、consumesCondition、
* producesCondition、methodsCondition、customConditionHolder
* 如果有一個不相等,則直接返回比較結果
*/
result = this.patternsCondition.compareTo(other.getPatternsCondition(), request);
if (result != 0) {
return result;
}
result = this.paramsCondition.compareTo(other.getParamsCondition(), request);
if (result != 0) {
return result;
}
result = this.headersCondition.compareTo(other.getHeadersCondition(), request);
if (result != 0) {
return result;
}
result = this.consumesCondition.compareTo(other.getConsumesCondition(), request);
if (result != 0) {
return result;
}
result = this.producesCondition.compareTo(other.getProducesCondition(), request);
if (result != 0) {
return result;
}
// Implicit (no method) vs explicit HTTP method mappings
result = this.methodsCondition.compareTo(other.getMethodsCondition(), request);
if (result != 0) {
return result;
}
result = this.customConditionHolder.compareTo(other.customConditionHolder, request);
if (result != 0) {
return result;
}
return 0;
}
關於各種 RequestCondition 請求條件就不一一分析了
getMappingPathPatterns
getMappingPathPatterns(RequestMappingInfo info)
方法,獲得 RequestMappingInfo 對應的請求路徑集合,代碼如下:
@Override
protected Set<String> getMappingPathPatterns(RequestMappingInfo info) {
return info.getPatternsCondition().getPatterns();
}
- 在 MappingRegistry 注冊表的
register
方法中的第3
步會調用,將所有符合的請求路徑與該 RequestMappingInfo 對象進行映射保存
getMatchingMapping
getMatchingMapping(RequestMappingInfo info, HttpServletRequest request)
方法,判斷請求是否匹配入參 RequestMappingInfo 對象,代碼如下:
@Override
protected RequestMappingInfo getMatchingMapping(RequestMappingInfo info, HttpServletRequest request) {
return info.getMatchingCondition(request);
}
- 在 AbstractHandlerMethodMapping 的
lookupHandlerMethod
獲取處理器方法的<1.1>
和<1.2>
會調用,遍歷所有的 Mapping 對象,獲取到該請求所匹配的 RequestMappingInfo 對象
handleMatch
handleMatch(RequestMappingInfo info, String lookupPath, HttpServletRequest request)
方法,覆寫父類的方法,設置更多的屬性到請求中,代碼如下:
@Override
protected void handleMatch(RequestMappingInfo info, String lookupPath, HttpServletRequest request) {
super.handleMatch(info, lookupPath, request);
// 獲得 bestPattern 和 uriVariables
String bestPattern; // 最佳路徑
Map<String, String> uriVariables; // 路徑上的變量集合
Set<String> patterns = info.getPatternsCondition().getPatterns();
if (patterns.isEmpty()) {
bestPattern = lookupPath;
uriVariables = Collections.emptyMap();
}
else {
bestPattern = patterns.iterator().next();
uriVariables = getPathMatcher().extractUriTemplateVariables(bestPattern, lookupPath);
}
request.setAttribute(BEST_MATCHING_PATTERN_ATTRIBUTE, bestPattern);
// 設置 MATRIX_VARIABLES_ATTRIBUTE 屬性,到請求中
if (isMatrixVariableContentAvailable()) {
Map<String, MultiValueMap<String, String>> matrixVars = extractMatrixVariables(request, uriVariables);
request.setAttribute(HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE, matrixVars);
}
// 設置 URI_TEMPLATE_VARIABLES_ATTRIBUTE 屬性,到請求中
Map<String, String> decodedUriVariables = getUrlPathHelper().decodePathVariables(request, uriVariables);
request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, decodedUriVariables);
// 設置 PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE 屬性,到請求中
if (!info.getProducesCondition().getProducibleMediaTypes().isEmpty()) {
Set<MediaType> mediaTypes = info.getProducesCondition().getProducibleMediaTypes();
request.setAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE, mediaTypes);
}
}
具體用途還不清楚😈
handleNoMatch
handleNoMatch(Set<RequestMappingInfo> infos, String lookupPath, HttpServletRequest request)
方法,覆寫父類方法,處理無匹配 Mapping 的情況
主要用途是,給出為什么找不到 Mapping 的原因,代碼如下:
@Override
protected HandlerMethod handleNoMatch(Set<RequestMappingInfo> infos, String lookupPath, HttpServletRequest request) throws ServletException {
// <1> 創建 PartialMatchHelper 對象,解析可能的錯誤
PartialMatchHelper helper = new PartialMatchHelper(infos, request);
if (helper.isEmpty()) {
return null;
}
// <2> 方法錯誤
if (helper.hasMethodsMismatch()) {
Set<String> methods = helper.getAllowedMethods();
if (HttpMethod.OPTIONS.matches(request.getMethod())) {
HttpOptionsHandler handler = new HttpOptionsHandler(methods);
return new HandlerMethod(handler, HTTP_OPTIONS_HANDLE_METHOD);
}
throw new HttpRequestMethodNotSupportedException(request.getMethod(), methods);
}
// <3> 可消費的 Content-Type 錯誤
if (helper.hasConsumesMismatch()) {
Set<MediaType> mediaTypes = helper.getConsumableMediaTypes();
MediaType contentType = null;
if (StringUtils.hasLength(request.getContentType())) {
try {
contentType = MediaType.parseMediaType(request.getContentType());
}
catch (InvalidMediaTypeException ex) {
throw new HttpMediaTypeNotSupportedException(ex.getMessage());
}
}
throw new HttpMediaTypeNotSupportedException(contentType, new ArrayList<>(mediaTypes));
}
// <4> 可生產的 Content-Type 錯誤
if (helper.hasProducesMismatch()) {
Set<MediaType> mediaTypes = helper.getProducibleMediaTypes();
throw new HttpMediaTypeNotAcceptableException(new ArrayList<>(mediaTypes));
}
// <5> 參數錯誤
if (helper.hasParamsMismatch()) {
List<String[]> conditions = helper.getParamConditions();
throw new UnsatisfiedServletRequestParameterException(conditions, request.getParameterMap());
}
return null;
}
-
核心代碼在 PartialMatchHelper 中實現,暫時忽略😈
-
方法錯誤。這是一個非常常見的錯誤,例如說
POST user/login
存在,但是我們請求了GET user/login
-
可消費的 Content-Type 錯誤
-
可生產的 Content-Type 錯誤
-
參數錯誤
RequestMappingHandlerMapping
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
,實現 MatchableHandlerMapping、EmbeddedValueResolverAware 接口,繼承 RequestMappingInfoHandlerMapping 抽象類,基於@RequestMapping
注解來構建 RequestMappingInfo 對象
寫到這里有那么一點點感動,終於到最底層的實現類了😢
構造方法
public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping
implements MatchableHandlerMapping, EmbeddedValueResolverAware {
private boolean useSuffixPatternMatch = true;
private boolean useRegisteredSuffixPatternMatch = false;
private boolean useTrailingSlashMatch = true;
private Map<String, Predicate<Class<?>>> pathPrefixes = new LinkedHashMap<>();
private ContentNegotiationManager contentNegotiationManager = new ContentNegotiationManager();
@Nullable
private StringValueResolver embeddedValueResolver;
/**
* RequestMappingInfo 的構建器
*/
private RequestMappingInfo.BuilderConfiguration config = new RequestMappingInfo.BuilderConfiguration();
}
afterPropertiesSet
因為父類 AbstractHandlerMethodMapping 實現了 InitializingBean 接口,在 Sping 初始化該 Bean 的時候,會調用該方法,完成一些初始化工作,方法如下:
@Override
public void afterPropertiesSet() {
// 構建 RequestMappingInfo.BuilderConfiguration 對象
this.config = new RequestMappingInfo.BuilderConfiguration();
this.config.setUrlPathHelper(getUrlPathHelper());
this.config.setPathMatcher(getPathMatcher());
this.config.setSuffixPatternMatch(this.useSuffixPatternMatch);
this.config.setTrailingSlashMatch(this.useTrailingSlashMatch);
this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch);
this.config.setContentNegotiationManager(getContentNegotiationManager());
// 調用父類,初始化
super.afterPropertiesSet();
}
isHandler
是否還記得 AbstractHandlerMethodMapping 的這個抽象方法?在它的 processCandidateBean
方法中,掃描 Spring 中所有 Bean 時會調用,判斷是否需要掃描這個 Bean 中的方法,方法如下:
@Override
protected boolean isHandler(Class<?> beanType) {
// 判斷是否有 @Controller 或者 @RequestMapping 的注解
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
有 @Controller
或者 @RequestMapping
的注解的類才需要進行掃描,是不是很熟悉😈
getMappingForMethod
是否還記得 AbstractHandlerMethodMapping 的這個抽象方法?在它的 detectHandlerMethods
方法中,用於獲取 Method 方法對應的 Mapping 對象,方法如下:
@Override
@Nullable
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
// <1> 基於方法上的 @RequestMapping 注解,創建 RequestMappingInfo 對象
RequestMappingInfo info = createRequestMappingInfo(method);
if (info != null) {
// <2> 基於類上的 @RequestMapping 注解,合並進去
RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
if (typeInfo != null) {
info = typeInfo.combine(info);
}
// <3> 如果有前綴,則設置到 info 中
String prefix = getPathPrefix(handlerType);
if (prefix != null) {
info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info);
}
}
return info;
}
-
調用
createRequestMappingInfo(AnnotatedElement element)
方法,基於方法上的@RequestMapping
注解,創建RequestMappingInfo
對象@Nullable private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) { // <1> 獲得 @RequestMapping 注解 RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class); // <2> 獲得自定義的條件。目前都是空方法,可以無視 RequestCondition<?> condition = (element instanceof Class ? getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element)); // <3> 基於 @RequestMapping 注解,創建 RequestMappingInfo 對象 return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null); } protected RequestMappingInfo createRequestMappingInfo(RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) { // 創建 RequestMappingInfo.Builder 對象,設置對應屬性 RequestMappingInfo.Builder builder = RequestMappingInfo .paths(resolveEmbeddedValuesInPatterns(requestMapping.path())) .methods(requestMapping.method()) .params(requestMapping.params()) .headers(requestMapping.headers()) .consumes(requestMapping.consumes()) .produces(requestMapping.produces()) .mappingName(requestMapping.name()); if (customCondition != null) { builder.customCondition(customCondition); } // 創建 RequestMappingInfo 對象 return builder.options(this.config).build(); }
-
基於類上的
@RequestMapping
注解,合並進去 -
如果有前綴,則設置到
info
中
match
match(HttpServletRequest request, String pattern)
方法,執行匹配,代碼如下:
@Override
public RequestMatchResult match(HttpServletRequest request, String pattern) {
// <1> 為 `pattern` 創建一個 RequestMappingInfo 對象
RequestMappingInfo info = RequestMappingInfo.paths(pattern).options(this.config).build();
// <2> 獲得請求對應的 RequestMappingInfo 對象
RequestMappingInfo matchingInfo = info.getMatchingCondition(request);
if (matchingInfo == null) { // <3> 沒有匹配的 RequestMappingInfo 對象返回空
return null;
}
// <4> 獲得請求匹配到的路徑
Set<String> patterns = matchingInfo.getPatternsCondition().getPatterns();
// <5> 獲取請求路徑
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
// <6> 創建 RequestMatchResult 結果
return new RequestMatchResult(patterns.iterator().next(), lookupPath, getPathMatcher());
}
總結
在 Spring MVC 處理請求的過程中,需要通過 HandlerMapping 組件會為請求找到合適的 HandlerExecutionChain
處理器執行鏈,包含處理器(handler
)和攔截器們(interceptors
),該組件體系結構如下:

本文就紅色框中的內容進行了分析,基於 Method 進行匹配。例如,我們所熟知的 @RequestMapping
等注解的方式
在將紅色框中的類注入到 Spring 上下文時,會進行一些初始化工作,掃描 @Controller
或者 @RequestMapping
注解標注的 Bean 對象,會將帶有 @RequestMapping
注解(包括其子注解)解析成 RequestMappingInfo
對象。接下來,會將 RequestMappingInfo
、該方法對象
、該方法所在類對象
往 MappingRegistry
注冊表進行注冊,其中會生成 HandlerMethod
處理器(方法的所有信息)對象保存起來。當處理某個請求時,HandlerMapping 找到該請求對應的 HandlerMethod
處理器對象后,就可以通過反射調用相應的方法了
這部分內容包含了我們常用到 @Controller
和 @RequestMapping
注解,算是 HandlerMapping 組件的核心內容,看完之后有種茅塞頓開的感覺
回到以前的 Servlet 時代,我們需要編寫許多的 Servlet 去處理請求,然后在 web.xml 中進行配置,而 Spring MVC 讓你通過只要在類和方法上面添加 @Controller
或者 @RequestMapping
注解這種方式,就可以處理請求,因為所有的請求都交給了 DispatcherServlet 去處理。這樣是不是簡化了你的工作量,讓你專注於業務開發。
參考文章:芋道源碼《精盡 Spring MVC 源碼分析》