該系列文檔是本人在學習 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 組件涉及到的內容比較多,考慮到內容的排版,因此將這部份內容拆分紅了四個模塊,依次進行分析:websocket
先來回顧一下HandlerMapping 接口體系的結構:app
在《HandlerMapping 組件(一)之 AbstractHandlerMapping》文檔中已經分析了 HandlerMapping 組件的 AbstractHandlerMapping 抽象類基類socket
在《HandlerMapping 組件(三)之 AbstractHandlerMethodMapping》文檔中也已經分析了圖中紅色框部分的 AbstractHandlerMethodMapping 系,基於 Method 進行匹配。例如,咱們所熟知的 @RequestMapping 等註解的方式。ide
那麼本文就接着來分析圖中黃色框部分的 AbstractUrlHandlerMapping 系,基於 URL 進行匹配。例如 《基於 XML 配置的 Spring MVC 簡單的 HelloWorld 實例應用》 ,固然,目前這種方式已經基本不用了,被 @RequestMapping
等註解的方式所取代。不過,Spring MVC 內置的一些路徑匹配,仍是使用這種方式。
由於 AbstractUrlHandlerMapping 在實際開發基本不會涉及到,因此本文選讀,能夠直接查看總結部分
一共有五個子類,分紅兩條線:
其中,WebSocketHandlerMapping 是 spring-websocket
項目中的類,本文會無視它
因此,本文按照 AbstractUrlHandlerMapping、SimpleUrlHandlerMapping、AbstractDetectingUrlHandlerMapping、BeanNameUrlHandlerMapping 順序進行分析
先來回顧一下在 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)
抽象方法,獲取請求對應的處理器,該方法由子類去實現,也就上圖中黃色框和紅色框兩類子類,本文分析黃色框部份內容
org.springframework.web.servlet.handler.AbstractUrlHandlerMapping
,實現 MatchableHandlerMapping 接口,繼承 AbstractHandlerMapping 抽象類,以 URL 做爲 Handler 處理器 的 HandlerMapping 抽象類,提供 Handler 的獲取、註冊等等通用的骨架方法。
public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping implements MatchableHandlerMapping { /** * 根路徑("/")的處理器 */ @Nullable private Object rootHandler; /** * 使用後置的 / 匹配 */ private boolean useTrailingSlashMatch = false; /** * 是否延遲加載處理器,默認關閉 */ private boolean lazyInitHandlers = false; /** * 路徑和處理器的映射 * * KEY:路徑 {@link #lookupHandler(String, HttpServletRequest)} */ private final Map<String, Object> handlerMap = new LinkedHashMap<>(); }
registerHandler(String[] urlPaths, String beanName)
方法,註冊多個 URL 的處理器,方法以下:
protected void registerHandler(String[] urlPaths, String beanName) throws BeansException, IllegalStateException { Assert.notNull(urlPaths, "URL path array must not be null"); for (String urlPath : urlPaths) { registerHandler(urlPath, beanName); } } protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException { Assert.notNull(urlPath, "URL path must not be null"); Assert.notNull(handler, "Handler object must not be null"); Object resolvedHandler = handler; // Eagerly resolve handler if referencing singleton via name. // <1> 若是非延遲加載,而且 handler 爲 String 類型,而且仍是單例,則去獲取 String 對應的 Bean 對象 if (!this.lazyInitHandlers && handler instanceof String) { String handlerName = (String) handler; ApplicationContext applicationContext = obtainApplicationContext(); if (applicationContext.isSingleton(handlerName)) { resolvedHandler = applicationContext.getBean(handlerName); } } // <2> 得到 urlPath 對應的處理器 Object mappedHandler = this.handlerMap.get(urlPath); // <3> 檢驗 mappedHandler 是否已存在,若是已存在,而且不是當前 resolvedHandler 對象,則拋出異常 if (mappedHandler != null) { if (mappedHandler != resolvedHandler) { throw new IllegalStateException( "Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath + "]: There is already " + getHandlerDescription(mappedHandler) + " mapped."); } } else { // <4.1> 若是是 / 根路徑,則設置爲 rootHandler if (urlPath.equals("/")) { if (logger.isTraceEnabled()) { logger.trace("Root mapping to " + getHandlerDescription(handler)); } setRootHandler(resolvedHandler); } // <4.2> 若是是 /* 路徑,則設置爲默認處理器 else if (urlPath.equals("/*")) { if (logger.isTraceEnabled()) { logger.trace("Default mapping to " + getHandlerDescription(handler)); } setDefaultHandler(resolvedHandler); } // <4.3> 添加到 handlerMap 中 else { this.handlerMap.put(urlPath, resolvedHandler); if (logger.isTraceEnabled()) { logger.trace("Mapped [" + urlPath + "] onto " + getHandlerDescription(handler)); } } } }
遍歷 URL,依次註冊處理器
handler
爲 String 類型,而且仍是單例,則去獲取 String 對應的 Bean 對象,resolvedHandler
handlerMap
中得到 urlPath
對應的處理器resolvedHandler
對象,則拋出異常resolvedHandler
處理器保存
/
根路徑,則設置 resolvedHandler
爲 rootHandler
/*
路徑,則設置爲默認處理器 defaultHandler
(在父類中)handlerMap
中實現父類的 getHandlerInternal(HttpServletRequest request)
方法,得到處理器,方法以下:
@Override @Nullable protected Object getHandlerInternal(HttpServletRequest request) throws Exception { // <1> 得到請求的路徑 String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); // <2> 得到處理器 Object handler = lookupHandler(lookupPath, request); // <3> 若是找不處處理器,則使用 rootHandler 或 defaultHandler 處理器 if (handler == null) { // We need to care for the default handler directly, since we need to // expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well. Object rawHandler = null; // <3.1> 若是是根路徑,則使用 rootHandler 處理器 if ("/".equals(lookupPath)) { rawHandler = getRootHandler(); } // <3.2> 使用默認處理器 if (rawHandler == null) { rawHandler = getDefaultHandler(); } if (rawHandler != null) { // Bean name or resolved handler? // <3.3> 若是找到的處理器是 String 類型,則從容器中找到該 beanName 對應的 Bean 做爲處理器 if (rawHandler instanceof String) { String handlerName = (String) rawHandler; rawHandler = obtainApplicationContext().getBean(handlerName); } // <3.4> 空方法,校驗處理器。目前暫無子類實現該方法 validateHandler(rawHandler, request); // <3.5> 建立處理器(HandlerExecutionChain 對象) handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null); } } return handler; }
得到請求路徑
調用 lookupHandler(String urlPath, HttpServletRequest request)
方法,得到處理器,詳情見下文
若是找不處處理器,則使用 rootHandler
或 defaultHandler
處理器
/
根路徑,則使用 rootHandler
處理器defaultHandler
默認處理器validateHandler(Object handler, HttpServletRequest request)
,對處理器進行校驗,空方法,暫無子類實現該方法buildPathExposingHandler
方法,建立 HandlerExecutionChain 處理器執行鏈,賦值給handler
處理器,詳情見下文返回請求對應的handler
處理器
因此說這裏但會的處理器對象多是一個 HandlerExecutionChain 對象,用途目前不清楚😈 😈 先繼續往下看
lookupHandler(String urlPath, HttpServletRequest request)
方法,得到請求對應的處理器,方法以下:
@Nullable protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception { // Direct match? // <1.1> 狀況一,從 handlerMap 中,直接匹配處理器 Object handler = this.handlerMap.get(urlPath); if (handler != null) { // Bean name or resolved handler? // <1.2> 若是找到的處理器是 String 類型,則從容器中找到該 beanName 對應的 Bean 做爲處理器 if (handler instanceof String) { String handlerName = (String) handler; handler = obtainApplicationContext().getBean(handlerName); } // <1.3> 空方法,校驗處理器。目前暫無子類實現該方法 validateHandler(handler, request); // <1.4> 建立處理器 return buildPathExposingHandler(handler, urlPath, urlPath, null); } // Pattern match? List<String> matchingPatterns = new ArrayList<>(); // <2.1> 狀況二,Pattern 匹配合適的,並添加到 matchingPatterns 中 for (String registeredPattern : this.handlerMap.keySet()) { if (getPathMatcher().match(registeredPattern, urlPath)) { // 路徑經過Pattern匹配成功 matchingPatterns.add(registeredPattern); } else if (useTrailingSlashMatch()) { if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", urlPath)) { matchingPatterns.add(registeredPattern + "/"); } } } // <2.2> 得到首個匹配(最優)的結果 String bestMatch = null; Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath); if (!matchingPatterns.isEmpty()) { // 排序 matchingPatterns.sort(patternComparator); if (logger.isTraceEnabled() && matchingPatterns.size() > 1) { logger.trace("Matching patterns " + matchingPatterns); } bestMatch = matchingPatterns.get(0); } if (bestMatch != null) { // <2.3> 得到 bestMatch 對應的處理器 handler = this.handlerMap.get(bestMatch); if (handler == null) { if (bestMatch.endsWith("/")) { handler = this.handlerMap.get(bestMatch.substring(0, bestMatch.length() - 1)); } if (handler == null) { // 若是得到不到,拋出 IllegalStateException 異常 throw new IllegalStateException( "Could not find handler for best pattern match [" + bestMatch + "]"); } } // <2.4> 若是找到的處理器是 String 類型,則從容器中找到該 beanName 對應的 Bean 做爲處理器 // Bean name or resolved handler? if (handler instanceof String) { String handlerName = (String) handler; handler = obtainApplicationContext().getBean(handlerName); } // <2.5> 空方法,校驗處理器。目前暫無子類實現該方法 validateHandler(handler, request); // <2.6> 得到匹配的路徑 String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestMatch, urlPath); // There might be multiple 'best patterns', let's make sure we have the correct URI template variables // for all of them // <2.7> 得到路徑參數集合 Map<String, String> uriTemplateVariables = new LinkedHashMap<>(); for (String matchingPattern : matchingPatterns) { if (patternComparator.compare(bestMatch, matchingPattern) == 0) { Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath); Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars); uriTemplateVariables.putAll(decodedVars); } } if (logger.isTraceEnabled() && uriTemplateVariables.size() > 0) { logger.trace("URI variables " + uriTemplateVariables); } // <2.8> 建立處理器 return buildPathExposingHandler(handler, bestMatch, pathWithinMapping, uriTemplateVariables); } // No handler found... return null; }
狀況一
handlerMap
中,直接匹配處理器buildPathExposingHandler
方法,詳情見下文狀況二
matchingPatterns
中bestMatch
bestMatch
對應的處理器,若是得到不到,拋出異常pathWithinMapping
uriTemplateVariables
buildPathExposingHandler
方法,詳情見下文都不匹配則返回 null
buildPathExposingHandler(Object rawHandler, String bestMatchingPattern, String pathWithinMapping, @Nullable Map<String, String> uriTemplateVariables)
方法
構建一個 HandlerExecutionChain 類型的處理器,添加兩個攔截器,方法以下:
protected Object buildPathExposingHandler(Object rawHandler, String bestMatchingPattern, String pathWithinMapping, @Nullable Map<String, String> uriTemplateVariables) { // <1> 建立 HandlerExecutionChain 對象 HandlerExecutionChain chain = new HandlerExecutionChain(rawHandler); // <2> 添加 PathExposingHandlerInterceptor 攔截器,到 chain 中 chain.addInterceptor(new PathExposingHandlerInterceptor(bestMatchingPattern, pathWithinMapping)); if (!CollectionUtils.isEmpty(uriTemplateVariables)) { // <3> 添加 UriTemplateVariablesHandlerInterceptor 攔截器,到 chain 中 chain.addInterceptor(new UriTemplateVariablesHandlerInterceptor(uriTemplateVariables)); } return chain; }
bestMatchingPattern
屬性到請求中uriTemplateVariables
屬性到請求中兩個攔截器以下:
private class PathExposingHandlerInterceptor extends HandlerInterceptorAdapter { /** 最佳匹配的路徑 */ private final String bestMatchingPattern; /** 被匹配的路徑 */ private final String pathWithinMapping; public PathExposingHandlerInterceptor(String bestMatchingPattern, String pathWithinMapping) { this.bestMatchingPattern = bestMatchingPattern; this.pathWithinMapping = pathWithinMapping; } @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { exposePathWithinMapping(this.bestMatchingPattern, this.pathWithinMapping, request); request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, handler); request.setAttribute(INTROSPECT_TYPE_LEVEL_MAPPING, supportsTypeLevelMappings()); return true; } } protected void exposePathWithinMapping(String bestMatchingPattern, String pathWithinMapping, HttpServletRequest request) { request.setAttribute(BEST_MATCHING_PATTERN_ATTRIBUTE, bestMatchingPattern); request.setAttribute(PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, pathWithinMapping); } private class UriTemplateVariablesHandlerInterceptor extends HandlerInterceptorAdapter { private final Map<String, String> uriTemplateVariables; public UriTemplateVariablesHandlerInterceptor(Map<String, String> uriTemplateVariables) { this.uriTemplateVariables = uriTemplateVariables; } @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { exposeUriTemplateVariables(this.uriTemplateVariables, request); return true; } } protected void exposeUriTemplateVariables(Map<String, String> uriTemplateVariables, HttpServletRequest request) { request.setAttribute(URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriTemplateVariables); }
都是往請求中設置相關屬性,用途目前不清楚😈 😈 先繼續往下看
match(HttpServletRequest request, String pattern)
方法,執行匹配,代碼以下:
@Override @Nullable public RequestMatchResult match(HttpServletRequest request, String pattern) { // <1> 得到請求路徑 String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); // <2> 模式匹配,若匹配,則返回 RequestMatchResult 對象 if (getPathMatcher().match(pattern, lookupPath)) { return new RequestMatchResult(pattern, lookupPath, getPathMatcher()); } else if (useTrailingSlashMatch()) { if (!pattern.endsWith("/") && getPathMatcher().match(pattern + "/", lookupPath)) { return new RequestMatchResult(pattern + "/", lookupPath, getPathMatcher()); } } return null; }
org.springframework.web.servlet.handler.SimpleUrlHandlerMapping
,繼承 AbstractUrlHandlerMapping 抽象類,簡單的就 URL 匹配的 HandlerMapping 實現類
在接觸 Spring MVC 比較早,你也許見過這樣配置
<!-- 定義一個 helloController Bean,實現了 Controller 接口 --> <bean id="helloController" class="com.fullmoon.study.controller.HelloController"/> <!-- 定義請求處理映射 HandlerMapping --> <bean class="org.springframework.web.servlet.handler. SimpleUrlHandlerMapping"> <property name="mappings" ref="urlMappings" /> </bean> <!-- 定義請求映射表 map --> <util:properties id="urlMappings"> <prop key="/hello.form">helloController</prop> </util:properties>
固然,上述這種配置基本已經不存在了,由於被 @RequestMapping
註解這樣的方式所取代。更多的是 Spring MVC 本身內部的組件可能在使用這種類型的 HandlerMapping ,例以下圖:
public class SimpleUrlHandlerMapping extends AbstractUrlHandlerMapping { /** * 配置的 URL 與處理器的映射 * * 最終,會調用 {@link #registerHandlers(Map)} 進行註冊到 {@link AbstractUrlHandlerMapping#handlerMap} 中 */ private final Map<String, Object> urlMap = new LinkedHashMap<>(); public void setMappings(Properties mappings) { CollectionUtils.mergePropertiesIntoMap(mappings, this.urlMap); } public void setUrlMap(Map<String, ?> urlMap) { this.urlMap.putAll(urlMap); } }
例如上面的配置示例就會經過 setMappings(Properties mappings)
方法,將 /hello.form
與 HelloController
設置到 urlMap
中
因此說處理器也多是一個 Controller 接口
initApplicationContext()
方法,用於初始化,將 urlMap
中的URL與處理器添加到父類的 handlerMap
中
在父類 WebApplicationObjectSupport 的父類 ApplicationObjectSupport 中能夠看到,由於實現了 ApplicationContextAware 接口,則在初始化該 Bean 的時候會調用
setApplicationContext(@Nullable ApplicationContext context)
方法,在這個方法中會調用initApplicationContext()
這個方法在父類 AbstractHandlerMapping 中,該方法會初始化攔截器們
@Override public void initApplicationContext() throws BeansException { super.initApplicationContext(); registerHandlers(this.urlMap); } protected void registerHandlers(Map<String, Object> urlMap) throws BeansException { if (urlMap.isEmpty()) { logger.trace("No patterns in " + formatMappingName()); } else { urlMap.forEach((url, handler) -> { // Prepend with slash if not already present. if (!url.startsWith("/")) { url = "/" + url; } // Remove whitespace from handler bean name. if (handler instanceof String) { handler = ((String) handler).trim(); } // 【核心代碼】註冊處理器 registerHandler(url, handler); }); if (logger.isDebugEnabled()) { List<String> patterns = new ArrayList<>(); if (getRootHandler() != null) { patterns.add("/"); } if (getDefaultHandler() != null) { patterns.add("/**"); } patterns.addAll(getHandlerMap().keySet()); logger.debug("Patterns " + patterns + " in " + formatMappingName()); } } }
邏輯很簡單,調用父類的registerHandler(String urlPath, Object handler)
方法,添加註冊器
org.springframework.web.servlet.handler.AbstractDetectingUrlHandlerMapping
,繼承 AbstractUrlHandlerMapping 抽象類,自動探測的基於 URL 匹配的 HandlerMapping 抽象實現類
public abstract class AbstractDetectingUrlHandlerMapping extends AbstractUrlHandlerMapping { /** * 是否只掃描可訪問的 Handler 們 */ private boolean detectHandlersInAncestorContexts = false; public void setDetectHandlersInAncestorContexts(boolean detectHandlersInAncestorContexts) { this.detectHandlersInAncestorContexts = detectHandlersInAncestorContexts; } }
initApplicationContext()
方法,用於初始化,找到符合條件的處理器,添加到父類的 handlerMap
中
在父類 WebApplicationObjectSupport 的父類 ApplicationObjectSupport 中能夠看到,由於實現了 ApplicationContextAware 接口,則在初始化該 Bean 的時候會調用
setApplicationContext(@Nullable ApplicationContext context)
方法,在這個方法中會調用initApplicationContext()
這個方法在父類 AbstractHandlerMapping 中,該方法會初始化攔截器們
@Override public void initApplicationContext() throws ApplicationContextException { super.initApplicationContext(); // 自動探測處理器 detectHandlers(); } protected void detectHandlers() throws BeansException { // <1> 從 Spring 上下文獲取全部 Object 類型的 Bean 的名稱們 ApplicationContext applicationContext = obtainApplicationContext(); String[] beanNames = (this.detectHandlersInAncestorContexts ? BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class) : applicationContext.getBeanNamesForType(Object.class)); // Take any bean name that we can determine URLs for. // <2> 遍歷全部的 Bean ,逐個註冊 for (String beanName : beanNames) { // <2.1> 得到 Bean 對應的 URL 們 String[] urls = determineUrlsForHandler(beanName); // <2.2> 若是該 Bean 存在對應的 URL,則添加該處理器 if (!ObjectUtils.isEmpty(urls)) { // 調用父類的方法,往 `handlerMap` 中添加註冊器 registerHandler(urls, beanName); } } if ((logger.isDebugEnabled() && !getHandlerMap().isEmpty()) || logger.isTraceEnabled()) { logger.debug("Detected " + getHandlerMap().size() + " mappings in " + formatMappingName()); } }
determineUrlsForHandler(String beanName)
抽象方法,交由子類實現,詳情見 BeanNameUrlHandlerMapping
handlerMap
中添加註冊器org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping
,繼承 AbstractDetectingUrlHandlerMapping 抽象類,基於 Bean 的名字來自動探測的 HandlerMapping 實現類
<!-- 定義一個 helloController Bean,實現了 Controller 接口 --> <bean id="/hello.form" class="com.fullmoon.study.controller.HelloController"/>
和 SimpleUrlHandlerMapping 不一樣,只須要設置它的 beanName 以 /
開頭就行了,會被 BeanNameUrlHandlerMapping 探測到
public class BeanNameUrlHandlerMapping extends AbstractDetectingUrlHandlerMapping { @Override protected String[] determineUrlsForHandler(String beanName) { List<String> urls = new ArrayList<>(); // 若是是以 / 開頭,添加到 urls if (beanName.startsWith("/")) { urls.add(beanName); } // 得到 beanName 的別名們,若是以 / 開頭,則添加到 urls String[] aliases = obtainApplicationContext().getAliases(beanName); for (String alias : aliases) { if (alias.startsWith("/")) { urls.add(alias); } } return StringUtils.toStringArray(urls); } }
邏輯很簡單,若是 Bean 的名稱或者別名是以 /
開頭,則會做爲一個 url
返回,父類則會將該 Bean 做爲一個處理器
在 Spring MVC 處理請求的過程當中,須要經過 HandlerMapping 組件會爲請求找到合適的 HandlerExecutionChain
處理器執行鏈,包含處理器(handler
)和攔截器們(interceptors
),該組件體系結構以下:
本文就黃色框中的內容進行了分析,基於 URL 進行匹配。若是你接觸 Spring MVC 較早,可能見過 SimpleUrlHandlerMapping
和 BeanNameUrlHandlerMapping
中的使用示例的配置方式。固然,目前這種方式已經基本不用了,被 @RequestMapping
等註解的方式所取代。不過,Spring MVC 內置的一些路徑匹配,仍是使用這種方式。
相對來講邏輯比較簡單,若是你有一個 Controller
或者 HttpRequestHandler
接口的實現類,有如下兩種方式將其設置爲處理器,能夠處理請求
Map<String, Object> urlMap
中添加 url
與 Controller 實現類
的映射就行了Controller 實現類
的 beanName
爲以 /
開頭的名稱就行了,它會探測到,將這個 Bean 的 beanName
做爲 url
,將 Controller 實現類
做爲處理器至此,HandlerMapping 組件就分析到這裏了,相信你對 HandlerMapping 組件有了一個深刻的瞭解,更加的清楚 Spring MVC 是如何處理器請求的
HandlerMapping 組件返回的
HandlerExecutionChain
處理器執行鏈,包含處理器(handler
)和攔截器們(interceptors
),那麼這個處理器是被誰調用的呢?由於不一樣的 HandlerMapping 實現類返回的處理器類型可能不同,如何執行這個處理器,這部分工做都交由 HandlerAdapter 組件(處理器的適配器)來完成
這裏咱們就把處理器理解爲
HandlerMethod
處理器對象吧,由於咱們平時使用最多的方式就是經過@RequestMapping
註解來標註某個方法處理對應的請求別慌,接下來分析 HandlerAdapter 組件不會特別複雜😈
參考文章:芋道源碼《精盡 Spring MVC 源碼分析》