精盡Spring MVC源碼分析 – HandlerMapping 組件(一)之 AbstractHandlerMapping

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

Spring 版本:5.2.4.RELEASE

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

HandlerMapping 組件

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

  • handler 處理器是 Object 類型,可以將其理解成 HandlerMethod 對象(例如我們使用最多的 @RequestMapping 註解所標註的方法會解析成該對象),包含了方法的所有資訊,通過該對象能夠執行該方法

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

由於 HandlerMapping 組件涉及到的內容比較多,考慮到內容的排版,所以將這部分內容拆分成了四個模組,依次進行分析:

HandlerMapping 組件(一)之 AbstractHandlerMapping

先來回顧一下在 DispatcherServlet 中處理請求的過程中哪裡使用到 HandlerMapping 組件,可以回到《一個請求的旅行過程》中的 DispatcherServletdoDispatch 方法中看看,如下:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HttpServletRequest processedRequest = request;
    HandlerExecutionChain mappedHandler = null;
    // ... 省略相關程式碼
    // Determine handler for the current request.
    // <3> 獲得請求對應的 HandlerExecutionChain 對象(HandlerMethod 和 HandlerInterceptor 攔截器們)
    mappedHandler = getHandler(processedRequest);
    if (mappedHandler == null) { // <3.1> 如果獲取不到,則根據配置拋出異常或返回 404 錯誤
        noHandlerFound(processedRequest, response);
        return;
    }
    // ... 省略相關程式碼
}

@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    if (this.handlerMappings != null) {
        // 遍歷 handlerMappings 組件們
        for (HandlerMapping mapping : this.handlerMappings) {
            // 通過 HandlerMapping 組件獲取到 HandlerExecutionChain 對象
            HandlerExecutionChain handler = mapping.getHandler(request);
            if (handler != null) {
                // 不為空則直接返回
                return handler;
            }
        }
    }
    return null;
}

通過遍歷 HandlerMapping 組件們,根據請求獲取到對應 HandlerExecutionChain 處理器執行鏈。注意,這裡是通過一個一個的 HandlerMapping 組件去進行處理,如果找到對應 HandlerExecutionChain 對象則直接返回,不會繼續下去,所以初始化的 HandlerMapping 組件是有一定的先後順序的,默認是BeanNameUrlHandlerMapping -> RequestMappingHandlerMapping

HandlerMapping 介面

org.springframework.web.servlet.HandlerMapping 介面,請求的處理器匹配器,負責為請求找到合適的 HandlerExecutionChain 處理器執行鏈,包含處理器(handler)和攔截器們(interceptors),程式碼如下:

public interface HandlerMapping {

	String BEST_MATCHING_HANDLER_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingHandler";

	String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping";

	String BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern";

	String INTROSPECT_TYPE_LEVEL_MAPPING = HandlerMapping.class.getName() + ".introspectTypeLevelMapping";

	String URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables";

	String MATRIX_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".matrixVariables";

	String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes";

	/**
	 * 獲得請求對應的處理器和攔截器們
	 */
	@Nullable
	HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}

類圖

HandlerMapping 介面體系的結構如下:

  • 藍色框 AbstractHandlerMapping 抽象類,實現了「為請求找到合適的 HandlerExecutionChain 處理器執行鏈」對應的的骨架邏輯,而暴露 getHandlerInternal(HttpServletRequest request) 抽象方法,交由子類實現。

  • AbstractHandlerMapping 的子類,分成兩派,分別是:

    • 黃色框 AbstractUrlHandlerMapping 系,基於 URL 進行匹配。例如 《基於 XML 配置的 Spring MVC 簡單的 HelloWorld 實例應用》 ,當然,目前這種方式已經基本不用了,被 @RequestMapping 等註解的方式所取代。不過,Spring MVC 內置的一些路徑匹配,還是使用這種方式。
    • 紅色框 AbstractHandlerMethodMapping 系,基於 Method 進行匹配。例如,我們所熟知的 @RequestMapping 等註解的方式。
  • 綠色框的 MatchableHandlerMapping 介面,定義了「判斷請求和指定 pattern 路徑是否匹配」的方法。

初始化過程

DispatcherServletinitHandlerMappings(ApplicationContext context) 方法,會在 onRefresh 方法被調用,初始化 HandlerMapping 組件,方法如下:

private void initHandlerMappings(ApplicationContext context) {
    // 置空 handlerMappings
    this.handlerMappings = null;

    // <1> 如果開啟探測功能,則掃描已註冊的 HandlerMapping 的 Bean 們,添加到 handlerMappings 中
    if (this.detectAllHandlerMappings) {
        // Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
        // 掃描已註冊的 HandlerMapping 的 Bean 們
        Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context,
                HandlerMapping.class, true, false);
        // 添加到 handlerMappings 中,並進行排序
        if (!matchingBeans.isEmpty()) {
            this.handlerMappings = new ArrayList<>(matchingBeans.values());
            // We keep HandlerMappings in sorted order.
            AnnotationAwareOrderComparator.sort(this.handlerMappings);
        }
    }
    // <2> 如果關閉探測功能,則獲得 Bean 名稱為 "handlerMapping" 對應的 Bean ,將其添加至 handlerMappings
    else {
        try {
            HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
            this.handlerMappings = Collections.singletonList(hm);
        }
        catch (NoSuchBeanDefinitionException ex) {
            // Ignore, we'll add a default HandlerMapping later.
        }
    }
    // Ensure we have at least one HandlerMapping, by registering
    // a default HandlerMapping if no other mappings are found.
    /**
     * <3> 如果未獲得到,則獲得默認配置的 HandlerMapping 類
     * {@link org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping}
     * {@link org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping}
     */
    if (this.handlerMappings == null) {
        this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
        if (logger.isTraceEnabled()) {
            logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
                    "': using default strategies from DispatcherServlet.properties");
        }
    }
}
  1. 如果「開啟」探測功能,則掃描已註冊的 HandlerMapping 的 Bean 們,添加到 handlerMappings 中,默認開啟

  2. 如果「關閉」探測功能,則獲得 Bean 名稱為 “handlerMapping” 對應的 Bean ,將其添加至 handlerMappings

  3. 如果未獲得到,則獲得默認配置的 HandlerMapping 類,調用 getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) 方法,就是從 DispatcherServlet.properties 文件中讀取 HandlerMapping 的默認實現類,如下:

    org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
    	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
    

    可以看到對應的是 BeanNameUrlHandlerMapping 和 RequestMappingHandlerMapping 對象

AbstractHandlerMapping

org.springframework.web.servlet.handler.AbstractHandlerMapping,實現 HandlerMapping、Ordered、BeanNameAware 介面,繼承 WebApplicationObjectSupport 抽象類

該類是 HandlerMapping 介面的抽象基類,實現了「為請求找到合適的 HandlerExecutionChain 處理器執行鏈」對應的的骨架邏輯,而暴露 getHandlerInternal(HttpServletRequest request) 抽象方法,交由子類實現

WebApplicationObjectSupport 抽象類,提供 applicationContext 屬性的聲明和注入。

構造方法

public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport implements HandlerMapping, Ordered, BeanNameAware {

	/**
	 * 默認處理器
	 */
	@Nullable
	private Object defaultHandler;

	/**
	 * URL 路徑工具類
	 */
	private UrlPathHelper urlPathHelper = new UrlPathHelper();

	/**
	 * 路徑匹配器
	 */
	private PathMatcher pathMatcher = new AntPathMatcher();

	/**
	 * 配置的攔截器數組.
	 *
	 * 在 {@link #initInterceptors()} 方法中,初始化到 {@link #adaptedInterceptors} 中
	 *
	 * 添加方式有兩種:
	 * 1. {@link #setInterceptors(Object...)} 方法
	 * 2. {@link #extendInterceptors(List)} 方法
	 */
	private final List<Object> interceptors = new ArrayList<>();

	/**
	 * 初始化後的攔截器 HandlerInterceptor 數組
	 */
	private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<>();

	private CorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource();

	private CorsProcessor corsProcessor = new DefaultCorsProcessor();

	private int order = Ordered.LOWEST_PRECEDENCE;  // default: same as non-Ordered

	/**
	 * 當前 Bean 的名稱
	 */
	@Nullable
	private String beanName;
    
    // ... 省略相關 getter、setter 方法
}
  • defaultHandler默認處理器,在獲得不到處理器時,可使用該屬性

  • interceptors配置的攔截器數組

  • adaptedInterceptors初始化後的攔截器 HandlerInterceptor 數組,也就是interceptors 轉換成的 HandlerInterceptor 攔截器對象

initApplicationContext

initApplicationContext()方法,用於初始化攔截器們,方法如下:

在父類 WebApplicationObjectSupport 的父類 ApplicationObjectSupport 中可以看到,因為實現了 ApplicationContextAware 介面,則在初始化該 Bean 的時候會調用 setApplicationContext(@Nullable ApplicationContext context) 方法,在這個方法中會調用 initApplicationContext() 這個方法

@Override
protected void initApplicationContext() throws BeansException {
    // <1> 空實現,交給子類實現,用於註冊自定義的攔截器到 interceptors 中,目前暫無子類實現
    extendInterceptors(this.interceptors);
    // <2> 掃描已註冊的 MappedInterceptor 的 Bean 們,添加到 mappedInterceptors 中
    detectMappedInterceptors(this.adaptedInterceptors);
    // <3> 將 interceptors 初始化成 HandlerInterceptor 類型,添加到 mappedInterceptors 中
    initInterceptors();
}
  1. 調用 extendInterceptors(List<Object> interceptors) 方法,空方法,目前暫無子類實現,暫時忽略

  2. 調用 detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) 方法,從 Spring 的上下文中,掃描已註冊的 MappedInterceptor 的攔截器們,添加到 adaptedInterceptors 中,方法如下:

    protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) {
        // 掃描已註冊的 MappedInterceptor 的 Bean 們,添加到 mappedInterceptors 中
        // MappedInterceptor 會根據請求路徑做匹配,是否進行攔截
        mappedInterceptors.addAll(BeanFactoryUtils
                .beansOfTypeIncludingAncestors(obtainApplicationContext(), MappedInterceptor.class, true, false)
                .values());
    }
    
  3. 調用 initInterceptors() 方法,將 interceptors 初始化成 HandlerInterceptor 類型,添加到 adaptedInterceptors 中,方法如下:

    protected void initInterceptors() {
        if (!this.interceptors.isEmpty()) {
            for (int i = 0; i < this.interceptors.size(); i++) {
                Object interceptor = this.interceptors.get(i);
                if (interceptor == null) {
                    throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null");
                }
                // 將 interceptors 初始化成 HandlerInterceptor 類型,添加到 mappedInterceptors 中
                // 注意,HandlerInterceptor 無需進行路徑匹配,直接攔截全部
                this.adaptedInterceptors.add(adaptInterceptor(interceptor));
            }
        }
    }
    protected HandlerInterceptor adaptInterceptor(Object interceptor) {
        if (interceptor instanceof HandlerInterceptor) {
            return (HandlerInterceptor) interceptor;
        }
        else if (interceptor instanceof WebRequestInterceptor) {
            return new WebRequestHandlerInterceptorAdapter((WebRequestInterceptor) interceptor);
        }
        else {
            throw new IllegalArgumentException("Interceptor type not supported: " + interceptor.getClass().getName());
        }
    }
    

關於攔截器在後文進行分析

getHandler

getHandler(HttpServletRequest request) 方法,獲得請求對應的 HandlerExecutionChain 處理器執行鏈,包含處理器(handler)和攔截器們(interceptors),方法如下:

@Override
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    // <1> 獲得處理器(HandlerMethod 或者 HandlerExecutionChain),該方法是抽象方法,由子類實現
    Object handler = getHandlerInternal(request);
    // <2> 獲得不到,則使用默認處理器
    if (handler == null) {
        handler = getDefaultHandler();
    }
    // <3> 還是獲得不到,則返回 null
    if (handler == null) {
        return null;
    }
    // Bean name or resolved handler?
    // <4> 如果找到的處理器是 String 類型,則從 Spring 容器中找到對應的 Bean 作為處理器
    if (handler instanceof String) {
        String handlerName = (String) handler;
        handler = obtainApplicationContext().getBean(handlerName);
    }

    // <5> 創建 HandlerExecutionChain 對象(包含處理器和攔截器)
    HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

    if (logger.isTraceEnabled()) {
        logger.trace("Mapped to " + handler);
    }
    else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
        logger.debug("Mapped to " + executionChain.getHandler());
    }

    if (CorsUtils.isCorsRequest(request)) {
        CorsConfiguration globalConfig = this.corsConfigurationSource.getCorsConfiguration(request);
        CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
        CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
        executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
    }

    return executionChain;
}
  1. 調用getHandlerInternal(HttpServletRequest request) 抽象方法,獲得 handler 處理器

  2. 如果 handler 處理器沒有找到,則調用getDefaultHandler() 方法,使用默認處理器,也就是 defaultHandler 屬性

  3. 如果 handler 處理器沒有找到,且沒有默認的處理器,則直接返回 null

  4. 如果找到的處理器是 String 類型,可能是 Bean 的名稱,則從 Spring 容器中找到對應的 Bean 作為處理器

  5. 調用 getHandlerExecutionChain(Object handler, HttpServletRequest request) 方法,獲得 HandlerExecutionChain 對象,方法如下:

    protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
        // <1> 創建 HandlerExecutionChain 對象
        HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ? (HandlerExecutionChain) handler
                : new HandlerExecutionChain(handler));
    
        // <2> 獲得請求路徑
        String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
        // <3> 遍歷 adaptedInterceptors 數組,獲得請求匹配的攔截器
        for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
            // 需要匹配,若路徑匹配,則添加到 chain 中
            if (interceptor instanceof MappedInterceptor) {
                MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
                if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) { // 匹配
                    chain.addInterceptor(mappedInterceptor.getInterceptor());
                }
            }
            // 無需匹配,直接添加到 chain 中
            else {
                chain.addInterceptor(interceptor);
            }
        }
        return chain
    }
    
    1. 創建一個 HandlerExecutionChain 對象,如果 handler 處理器就是該類型對象,則直接使用
    2. 獲得請求路徑
    3. 遍歷 adaptedInterceptors 攔截器數組,根據請求路徑獲得當前請求匹配的攔截器們,添加到 HandlerExecutionChain 對象中
  6. 返回上面創建的 HandlerExecutionChain 對象

MatchableHandlerMapping

org.springframework.web.servlet.handler.MatchableHandlerMapping,定義了「判斷請求和指定 pattern 路徑是否匹配」的方法。程式碼如下:

public interface MatchableHandlerMapping extends HandlerMapping {

	/**
	 * 判斷請求和指定 pattern 路徑是否匹配
	 */
	@Nullable
	RequestMatchResult match(HttpServletRequest request, String pattern);
}

RequestMatchResult

org.springframework.web.servlet.handler.RequestMatchResult 類,判斷請求和指定 pattern 路徑是否匹配時,返回的匹配結果,程式碼如下:

public class RequestMatchResult {
	/**
	 * 匹配到的路徑
	 */
	private final String matchingPattern;

	/**
	 * 被匹配的路徑
	 */
	private final String lookupPath;

	/**
	 * 路徑匹配器
	 */
	private final PathMatcher pathMatcher;

	public RequestMatchResult(String matchingPattern, String lookupPath, PathMatcher pathMatcher) {
		Assert.hasText(matchingPattern, "'matchingPattern' is required");
		Assert.hasText(lookupPath, "'lookupPath' is required");
		Assert.notNull(pathMatcher, "'pathMatcher' is required");
		this.matchingPattern = matchingPattern;
		this.lookupPath = lookupPath;
		this.pathMatcher = pathMatcher;
	}

	public Map<String, String> extractUriTemplateVariables() {
		return this.pathMatcher.extractUriTemplateVariables(this.matchingPattern, this.lookupPath);
	}
}

目前實現 MatchableHandlerMapping 介面的類,有 RequestMappingHandlerMapping 類和 AbstractUrlHandlerMapping 抽象類,在後續都會進行分析

總結

本文對 Spring MVC 處理請求的過程中使用到的 HandlerMapping 組件進行了分析,會為請求找到合適的 HandlerExecutionChain 處理器執行鏈,包含處理器(handler)和攔截器們(interceptors

HandlerMapping 組件的實現類分為兩種:

  • 基於 URL 進行匹配。例如 《基於 XML 配置的 Spring MVC 簡單的 HelloWorld 實例應用》 ,當然,目前這種方式已經基本不用了,被 @RequestMapping 等註解的方式所取代。不過,Spring MVC 內置的一些路徑匹配,還是使用這種方式
  • 基於 Method 進行匹配。例如,我們所熟知的 @RequestMapping 等註解的方式

AbstractHandlerMapping 抽象類,作為一個基類,實現了「為請求找到合適的 HandlerExecutionChain 處理器執行鏈」對應的的骨架邏輯,而暴露 getHandlerInternal(HttpServletRequest request) 抽象方法,交由子類實現。

本文對 HandlerMapping 組件做了一個簡單的介紹,更多的細節交由其子類去實現,由於涉及到的內容比較多,BeanNameUrlHandlerMappingRequestMappingHandlerMapping 兩種實現類則在後續的文檔中依次進行分析

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