該系列文檔是本人在學習 Spring MVC 的源碼過程當中總結下來的,可能對讀者不太友好,請結合個人源碼註釋 Spring MVC 源碼分析 GitHub 地址 進行閱讀html
Spring 版本:5.2.4.RELEASEjava
該系列其餘文檔請查看:《精盡 Spring MVC 源碼分析 - 文章導讀》git
HandlerMapping 組件,請求的處理器匹配器,負責爲請求找到合適的 HandlerExecutionChain
處理器執行鏈,包含處理器(handler
)和攔截器們(interceptors
)github
handler
處理器是 Object 類型,能夠將其理解成 HandlerMethod 對象(例如咱們使用最多的 @RequestMapping
註解所標註的方法會解析成該對象),包含了方法的全部信息,經過該對象可以執行該方法web
HandlerInterceptor
攔截器對處理請求進行加強處理,可用於在執行方法前、成功執行方法後、處理完成後進行一些邏輯處理spring
因爲 HandlerMapping 組件涉及到的內容比較多,考慮到內容的排版,因此將這部份內容拆分紅了四個模塊,依次進行分析:api
先來回顧一下HandlerMapping 接口體系的結構:數組
在《HandlerMapping 組件(一)之 AbstractHandlerMapping》文檔中已經分析了 HandlerMapping 組件的 AbstractHandlerMapping 抽象類基類安全
那麼本文就接着來分析圖中紅色框部分的 AbstractHandlerMethodMapping 系,該系是基於 Method 進行匹配。例如,咱們所熟知的 @RequestMapping
等註解的方式。一共就三個類,很少😈😈😈mvc
涉及到的內容比較多,能夠直接查看個人總結
先來回顧一下在 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 的請求匹配的註解,體系結構以下:
關於這些註解,你們已經很是熟悉了,各自的屬性就再也不進行講述了,可具體查看源碼:
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(); } }
@RequestMapping(name = "login", value = "user/login")
註解的方法,它對應的 Mapping 的名字就是 login
"#"
+ 方法名。例如,@RequestMapping(value = "user/login")
註解的方法,假設它所在的類爲 UserController ,對應的方法名爲 login ,則它對應的 Mapping 的名字就是 USERCONTROLLER#login
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(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 對象,詳情見下文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(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(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; }
@Controller
註解後,默認入參 handler 就是它的 beanName ,即 userController
因此你會發現 HandlerMethod 處理器對象,就是handler(方法所在類)
+method(方法對象)
的組合,經過它能執行該方法
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 對象
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 註冊時的一些信息
由於 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 處理器們,空方法,暫無子類實現
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); } }
isHandler(Class<?> beanType)
抽象方法,判斷 Bean 的類型是否須要處理,其 RequestMappingHandlerMapping
子類的實現:有 @Controller
或者 @RequestMapping
註解的 BeandetectHandlerMethods(Object handler)
方法,掃描 Bean 的方法進行處理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 處理器,就能夠經過反射執行對應的方法了
到這裏,思路是否是愈來愈清晰了,咱們繼續往下分析
因爲上面初始化涉及到內容有點多,先回到本文上面的回顧這一小節,經過 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(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
)
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))); } } }
若是匹配到,則獲取最佳匹配的 Match 結果的 HandlerMethod
屬性
matches
的結果,排序器lookupPath
到請求屬性HandlerMethod
處理器對象若是匹配不到,則處理不匹配的狀況,調用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);
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 不是 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(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(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(RequestMappingInfo info)
方法,得到 RequestMappingInfo 對應的請求路徑集合,代碼以下:
@Override protected Set<String> getMappingPathPatterns(RequestMappingInfo info) { return info.getPatternsCondition().getPatterns(); }
register
方法中的第 3
步會調用,將全部符合的請求路徑與該 RequestMappingInfo 對象進行映射保存getMatchingMapping(RequestMappingInfo info, HttpServletRequest request)
方法,判斷請求是否匹配入參 RequestMappingInfo 對象,代碼以下:
@Override protected RequestMappingInfo getMatchingMapping(RequestMappingInfo info, HttpServletRequest request) { return info.getMatchingCondition(request); }
lookupHandlerMethod
獲取處理器方法的<1.1>
和<1.2>
會調用,遍歷全部的 Mapping 對象,獲取到該請求所匹配的 RequestMappingInfo 對象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(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 錯誤
參數錯誤
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(); }
由於父類 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(); }
是否還記得 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
的註解的類才須要進行掃描,是否是很熟悉😈
是否還記得 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(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 源碼分析》