精盡Spring MVC源碼分析 - HandlerMapping 組件(四)之 AbstractUrlHandlerMapping

該系列文檔是本人在學習 Spring MVC 的源碼過程當中總結下來的,可能對讀者不太友好,請結合個人源碼註釋 Spring MVC 源碼分析 GitHub 地址 進行閱讀html

Spring 版本:5.2.4.RELEASEjava

該系列其餘文檔請查看:《精盡 Spring MVC 源碼分析 - 文章導讀》git

HandlerMapping 組件

HandlerMapping 組件,請求的處理器匹配器,負責爲請求找到合適的 HandlerExecutionChain 處理器執行鏈,包含處理器(handler)和攔截器們(interceptorsgithub

  • handler 處理器是 Object 類型,能夠將其理解成 HandlerMethod 對象(例如咱們使用最多的 @RequestMapping 註解所標註的方法會解析成該對象),包含了方法的全部信息,經過該對象可以執行該方法web

  • HandlerInterceptor 攔截器對處理請求進行加強處理,可用於在執行方法前、成功執行方法後、處理完成後進行一些邏輯處理spring

因爲 HandlerMapping 組件涉及到的內容比較多,考慮到內容的排版,因此將這部份內容拆分紅了四個模塊,依次進行分析:websocket

HandlerMapping 組件(四)之 AbstractUrlHandlerMapping

先來回顧一下HandlerMapping 接口體系的結構:app

《HandlerMapping 組件(一)之 AbstractHandlerMapping》文檔中已經分析了 HandlerMapping 組件的 AbstractHandlerMapping 抽象類基類socket

《HandlerMapping 組件(三)之 AbstractHandlerMethodMapping》文檔中也已經分析了圖中紅色框部分的 AbstractHandlerMethodMapping 系,基於 Method 進行匹配。例如,咱們所熟知的 @RequestMapping 等註解的方式。ide

那麼本文就接着來分析圖中黃色框部分的 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,依次註冊處理器

  1. 若是非延遲加載,而且 handler 爲 String 類型,而且仍是單例,則去獲取 String 對應的 Bean 對象,resolvedHandler
  2. handlerMap中得到 urlPath 對應的處理器
  3. 若是該路徑已存在對應的處理器,可是不是當前 resolvedHandler 對象,則拋出異常
  4. 不然,該路徑不存在對應的處理器,則將當前 resolvedHandler 處理器保存
    1. 若是是 / 根路徑,則設置 resolvedHandlerrootHandler
    2. 不然,若是是 /* 路徑,則設置爲默認處理器 defaultHandler(在父類中)
    3. 不然,添加到 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;
}
  1. 得到請求路徑

  2. 調用 lookupHandler(String urlPath, HttpServletRequest request) 方法,得到處理器,詳情見下文

  3. 若是找不處處理器,則使用 rootHandlerdefaultHandler 處理器

    1. 若是是/根路徑,則使用 rootHandler 處理器
    2. 不然,使用 defaultHandler 默認處理器
    3. 若是找到的處理器是 String 類型,則從容器中找到該 beanName 對應的 Bean 做爲處理器
    4. 調用validateHandler(Object handler, HttpServletRequest request),對處理器進行校驗,空方法,暫無子類實現該方法
    5. 調用 buildPathExposingHandler方法,建立 HandlerExecutionChain 處理器執行鏈,賦值給handler處理器,詳情見下文
  4. 返回請求對應的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;
}
  1. 狀況一

    1. handlerMap 中,直接匹配處理器
    2. 若是找到的處理器是 String 類型,則從容器中找到該 beanName 對應的 Bean 做爲處理器
    3. 校驗處理器,空方法,暫無子類實現,暫時忽略
    4. 建立處理器,直接返回,這裏是 HandlerExecutionChain 類型,調用 buildPathExposingHandler 方法,詳情見下文
  2. 狀況二

    1. Pattern 匹配合適的,並添加到 matchingPatterns
    2. 得到首個匹配(最優)的結果 bestMatch
    3. 得到 bestMatch 對應的處理器,若是得到不到,拋出異常
    4. 若是找到的處理器是 String 類型,則從容器中找到該 beanName 對應的 Bean 做爲處理器
    5. 校驗處理器,空方法,暫無子類實現,暫時忽略
    6. 得到請求最匹配的路徑pathWithinMapping
    7. 得到匹配的路徑參數集合uriTemplateVariables
    8. 建立處理器,直接返回,這裏是 HandlerExecutionChain 類型,調用 buildPathExposingHandler 方法,詳情見下文
  3. 都不匹配則返回 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;
}
  1. 建立 HandlerExecutionChain 類型的處理器對象
  2. 添加 PathExposingHandlerInterceptor 攔截器,用於暴露 bestMatchingPattern 屬性到請求中
  3. 添加 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;
}
  1. 得到請求路徑
  2. 模式匹配,若匹配,則返回 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.formHelloController 設置到 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());
    }
}
  1. 從 Spring 上下文獲取全部 Object 類型的 Bean 的名稱們
  2. 遍歷全部的 Bean ,逐個註冊
    1. 得到 Bean 對應的 URL 們,調用determineUrlsForHandler(String beanName)抽象方法,交由子類實現,詳情見 BeanNameUrlHandlerMapping
    2. 若是該 Bean 存在對應的 URL,則添加該處理器,調用父類的方法,往 handlerMap 中添加註冊器

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 較早,可能見過 SimpleUrlHandlerMappingBeanNameUrlHandlerMapping 中的使用示例的配置方式。固然,目前這種方式已經基本不用了,被 @RequestMapping 等註解的方式所取代。不過,Spring MVC 內置的一些路徑匹配,仍是使用這種方式。

相對來講邏輯比較簡單,若是你有一個 Controller 或者 HttpRequestHandler 接口的實現類,有如下兩種方式將其設置爲處理器,能夠處理請求

  • 配置 SimpleUrlHandlerMapping 類型的 HandlerMapping 對象,往它的 Map<String, Object> urlMap 中添加 urlController 實現類的映射就行了
  • 配置 BeanNameUrlHandlerMapping 類型的 HandlerMapping 對象,設置 Controller 實現類beanName 爲以 / 開頭的名稱就行了,它會探測到,將這個 Bean 的 beanName 做爲 url,將 Controller 實現類 做爲處理器

至此,HandlerMapping 組件就分析到這裏了,相信你對 HandlerMapping 組件有了一個深刻的瞭解,更加的清楚 Spring MVC 是如何處理器請求的

HandlerMapping 組件返回的 HandlerExecutionChain 處理器執行鏈,包含處理器(handler)和攔截器們(interceptors),那麼這個處理器是被誰調用的呢?

由於不一樣的 HandlerMapping 實現類返回的處理器類型可能不同,如何執行這個處理器,這部分工做都交由 HandlerAdapter 組件(處理器的適配器)來完成

這裏咱們就把處理器理解爲 HandlerMethod 處理器對象吧,由於咱們平時使用最多的方式就是經過 @RequestMapping 註解來標註某個方法處理對應的請求

別慌,接下來分析 HandlerAdapter 組件不會特別複雜😈

參考文章:芋道源碼《精盡 Spring MVC 源碼分析》

相關文章
相關標籤/搜索