精盡Spring MVC原始碼分析 - HandlerMapping 元件(一)之 AbstractHandlerMapping

月圓吖發表於2020-12-15

該系列文件是本人在學習 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 介面體系的結構如下:

精盡Spring MVC原始碼分析 - HandlerMapping 元件(一)之 AbstractHandlerMapping
  • 藍色框 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 原始碼分析》

相關文章