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

該系列文檔是本人在學習 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 組件涉及到的內容比較多,考慮到內容的排版,因此將這部份內容拆分紅了四個模塊,依次進行分析:數組

HandlerMapping 組件(一)之 AbstractHandlerMapping

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

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 -> RequestMappingHandlerMappingapp

HandlerMapping 接口

org.springframework.web.servlet.HandlerMapping 接口,請求的處理器匹配器,負責爲請求找到合適的 HandlerExecutionChain 處理器執行鏈,包含處理器(handler)和攔截器們(interceptors),代碼以下:cors

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 源碼分析》

相關文章
相關標籤/搜索