參考 知識星球 中 芋道源碼 星球的源碼解析,一個活躍度非常高的 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 組件(四)之 AbstractUrlHandlerMapping
先來回顧一下HandlerMapping 接口體系的結構:

在《HandlerMapping 組件(一)之 AbstractHandlerMapping》文檔中已經分析了 HandlerMapping 組件的 AbstractHandlerMapping 抽象類基類
在《HandlerMapping 組件(三)之 AbstractHandlerMethodMapping》文檔中也已經分析了圖中紅色框部分的 AbstractHandlerMethodMapping 系,基於 Method 進行匹配。例如,我們所熟知的 @RequestMapping 等注解的方式。
那么本文就接着來分析圖中黃色框部分的 AbstractUrlHandlerMapping 系,基於 URL 進行匹配。例如 《基於 XML 配置的 Spring MVC 簡單的 HelloWorld 實例應用》 ,當然,目前這種方式已經基本不用了,被 @RequestMapping
等注解的方式所取代。不過,Spring MVC 內置的一些路徑匹配,還是使用這種方式。
因為 AbstractUrlHandlerMapping 在實際開發基本不會涉及到,所以本文選讀,可以直接查看總結部分
一共有五個子類,分成兩條線:
- AbstractUrlHandlerMapping <= SimpleUrlHandlerMapping <= WebSocketHandlerMapping
- AbstractUrlHandlerMapping <= AbstractDetectingUrlHandlerMapping <= BeanNameUrlHandlerMapping
其中,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)
抽象方法,獲取請求對應的處理器,該方法由子類去實現,也就上圖中黃色框和紅色框兩類子類,本文分析黃色框部分內容
AbstractUrlHandlerMapping
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
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
實現父類的 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
默認處理器 - 如果找到的處理器是 String 類型,則從容器中找到該 beanName 對應的 Bean 作為處理器
- 調用
validateHandler(Object handler, HttpServletRequest request)
,對處理器進行校驗,空方法,暫無子類實現該方法 - 調用
buildPathExposingHandler
方法,創建 HandlerExecutionChain 處理器執行鏈,賦值給handler
處理器,詳情見下文
- 如果是
-
返回請求對應的
handler
處理器
所以說這里但會的處理器對象可能是一個 HandlerExecutionChain 對象,用途目前不清楚😈 😈 先繼續往下看
lookupHandler
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
中,直接匹配處理器 - 如果找到的處理器是 String 類型,則從容器中找到該 beanName 對應的 Bean 作為處理器
- 校驗處理器,空方法,暫無子類實現,暫時忽略
- 創建處理器,直接返回,這里是 HandlerExecutionChain 類型,調用
buildPathExposingHandler
方法,詳情見下文
- 從
-
情況二
- Pattern 匹配合適的,並添加到
matchingPatterns
中 - 獲得首個匹配(最優)的結果
bestMatch
- 獲得
bestMatch
對應的處理器,如果獲得不到,拋出異常 - 如果找到的處理器是 String 類型,則從容器中找到該 beanName 對應的 Bean 作為處理器
- 校驗處理器,空方法,暫無子類實現,暫時忽略
- 獲得請求最匹配的路徑
pathWithinMapping
- 獲得匹配的路徑參數集合
uriTemplateVariables
- 創建處理器,直接返回,這里是 HandlerExecutionChain 類型,調用
buildPathExposingHandler
方法,詳情見下文
- Pattern 匹配合適的,並添加到
-
都不匹配則返回
null
buildPathExposingHandler
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;
}
- 創建 HandlerExecutionChain 類型的處理器對象
- 添加 PathExposingHandlerInterceptor 攔截器,用於暴露
bestMatchingPattern
屬性到請求中 - 添加 UriTemplateVariablesHandlerInterceptor 攔截器,用於暴露
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
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;
}
- 獲得請求路徑
- 模式匹配,若匹配,則返回 RequestMatchResult 對象
SimpleUrlHandlerMapping
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
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)
方法,添加注冊器
AbstractDetectingUrlHandlerMapping
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
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());
}
}
- 從 Spring 上下文獲取所有 Object 類型的 Bean 的名稱們
- 遍歷所有的 Bean ,逐個注冊
- 獲得 Bean 對應的 URL 們,調用
determineUrlsForHandler(String beanName)
抽象方法,交由子類實現,詳情見BeanNameUrlHandlerMapping
- 如果該 Bean 存在對應的 URL,則添加該處理器,調用父類的方法,往
handlerMap
中添加注冊器
- 獲得 Bean 對應的 URL 們,調用
BeanNameUrlHandlerMapping
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 探測到
determineUrlsForHandler
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
接口的實現類,有以下兩種方式將其設置為處理器,可以處理請求
- 配置 SimpleUrlHandlerMapping 類型的 HandlerMapping 對象,往它的
Map<String, Object> urlMap
中添加url
與Controller 實現類
的映射就好了 - 配置 BeanNameUrlHandlerMapping 類型的 HandlerMapping 對象,設置
Controller 實現類
的beanName
為以/
開頭的名稱就好了,它會探測到,將這個 Bean 的beanName
作為url
,將Controller 實現類
作為處理器
至此,HandlerMapping 組件就分析到這里了,相信你對 HandlerMapping 組件有了一個深入的了解,更加的清楚 Spring MVC 是如何處理器請求的
HandlerMapping 組件返回的
HandlerExecutionChain
處理器執行鏈,包含處理器(handler
)和攔截器們(interceptors
),那么這個處理器是被誰調用的呢?因為不同的 HandlerMapping 實現類返回的處理器類型可能不一樣,如何執行這個處理器,這部分工作都交由 HandlerAdapter 組件(處理器的適配器)來完成
這里我們就把處理器理解為
HandlerMethod
處理器對象吧,因為我們平時使用最多的方式就是通過@RequestMapping
注解來標注某個方法處理對應的請求別慌,接下來分析 HandlerAdapter 組件不會特別復雜😈
參考文章:芋道源碼《精盡 Spring MVC 源碼分析》