1. 程式人生 > 其它 >Spring 註解面面通 之 @RequestMapping 註冊處理方法原始碼解析

Spring 註解面面通 之 @RequestMapping 註冊處理方法原始碼解析

技術標籤:Spring 全面解析SpringRequestMapping註解註冊

  @RequestMapping原始碼解析主要分為兩個階段:

  ① @RequestMapping註釋的方法掃描註冊。

  ② 請求匹配@RequestMapping註釋的方法。

  本文針對第階段從原始碼角度進行解析,關於第階段請參照《Spring 註解面面通 之 @RequestMapping 請求匹配方法原始碼解析》

  注意:@RequestMapping註釋方法掃描註冊的起點是RequestMappingHandlerMapping.afterPropertiesSet()

  <annotation-driven />

配置

  為何要先說<annotation-driven />配置呢?

  @RequestMapping註釋方法掃描註冊,自RequestMappingHandlerMapping.afterPropertiesSet()開始,RequestMappingHandlerMapping實現了InitializingBean介面,重寫方法afterPropertiesSet()

  RequestMappingHandlerMapping正是在<annotation-driven />配置解析時註冊到BeanFactory中。

/**
 * 解析<annotation-driven />配置.
 */
@Override @Nullable public BeanDefinition parse(Element element, ParserContext parserContext) { Object source = parserContext.extractSource(element); XmlReaderContext readerContext = parserContext.getReaderContext(); CompositeComponentDefinition compDefinition = new CompositeComponentDefinition
(element.getTagName(), source); parserContext.pushContainingComponent(compDefinition); // 建立ContentNegotiationManager. RuntimeBeanReference contentNegotiationManager = getContentNegotiationManager(element, source, parserContext); // 建立RequestMappingHandlerMapping的Bean定義. RootBeanDefinition handlerMappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class); handlerMappingDef.setSource(source); handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); // 設定排序序號. handlerMappingDef.getPropertyValues().add("order", 0); // 設定ContentNegotiationManager. handlerMappingDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager); // 矩陣變數enable-matrix-variables屬性解析. if (element.hasAttribute("enable-matrix-variables")) { Boolean enableMatrixVariables = Boolean.valueOf(element.getAttribute("enable-matrix-variables")); handlerMappingDef.getPropertyValues().add("removeSemicolonContent", !enableMatrixVariables); } // 呼叫configurePathMatchingProperties(...)配置RequestMappingHandlerMapping屬性. // 處理屬性包括:useSuffixPatternMatch、useTrailingSlashMatch、useRegisteredSuffixPatternMatch、urlPathHelper、pathMatcher. configurePathMatchingProperties(handlerMappingDef, element, parserContext); readerContext.getRegistry().registerBeanDefinition(HANDLER_MAPPING_BEAN_NAME , handlerMappingDef); // 設定CORS相關配置. RuntimeBeanReference corsRef = MvcNamespaceUtils.registerCorsConfigurations(null, parserContext, source); handlerMappingDef.getPropertyValues().add("corsConfigurations", corsRef); ...... // 註冊RequestMappingHandlerMapping定義. parserContext.registerComponent(new BeanComponentDefinition(handlerMappingDef, HANDLER_MAPPING_BEAN_NAME)); ...... return null; }

  @RequestMapping註釋方法掃描註冊

  @RequestMapping註釋方法掃描註冊流程:

在這裡插入圖片描述

  1) RequestMappingHandlerMapping.afterPropertiesSet()方法。

  ① 建立RequestMappingInfo構建配置,設定到RequestMappingHandlerMappingconfig屬性中。

  ② 設定configurlPathHelperpathMatchersuffixPatternMatchtrailingSlashMatchristeredSuffixPatternMatchcontentNegotiationManager屬性。

  ③ 呼叫AbstractHandlerMethodMapping.afterPropertiesSet()繼續處理。

/**
 * Bean設定屬性後,初始化呼叫.
 */
@Override
public void afterPropertiesSet() {
    // 建立RequestMappingInfo構建配置.
    this.config = new RequestMappingInfo.BuilderConfiguration();
    // 設定UrlPathHelper.
    this.config.setUrlPathHelper(getUrlPathHelper());
    // 設定PathMatcher.
    this.config.setPathMatcher(getPathMatcher());
    // 設定SuffixPatternMatch.
    this.config.setSuffixPatternMatch(this.useSuffixPatternMatch);
    // 設定TrailingSlashMatch.
    this.config.setTrailingSlashMatch(this.useTrailingSlashMatch);
    // 設定RegisteredSuffixPatternMatch.
    this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch);
    // 設定ContentNegotiationManager.
    this.config.setContentNegotiationManager(getContentNegotiationManager());
    // 呼叫AbstractHandlerMethodMapping.afterPropertiesSet().
    super.afterPropertiesSet();
}

​  2) AbstractHandlerMethodMapping.initHandlerMethods()方法。

  ① 從ApplicationContext總取得所有以註冊的Bean名稱陣列。

  ② 遍歷從ApplicationContext總取得所有以註冊的Bean名稱陣列。

  ③ 若Bean的名稱以scopedTarget.開頭,不予處理此類Bean

  ④ 根據當前Bean名稱獲取對應的Bean型別。

  ⑤ 若當前Bean型別不是由@Cotroller@RequestMapping註釋,不予處理此類Bean

  ⑥ 從處理器中查詢處理方法,並註冊到AbstractHandlerMethodMappingmappingRegistry屬性中。

/**
 * 掃描ApplicationContext中的Bean,檢測並註冊處理程式方法.
 */
protected void initHandlerMethods() {
    if (logger.isDebugEnabled()) {
        logger.debug("Looking for request mappings in application context: " + getApplicationContext());
    }
    // 取得ApplicationContext中註冊的所有Bean.
    String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
                          BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
                          obtainApplicationContext().getBeanNamesForType(Object.class));
    // 遍歷ApplicationContext中註冊的所有Bean.
    for (String beanName : beanNames) {
        // 不以"scopedTarget."開頭.
        if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
            Class<?> beanType = null;
            try {
                beanType = obtainApplicationContext().getType(beanName);
            }
            catch (Throwable ex) {
                // 一個無法解析的bean型別,可能來自一個延遲載入的Bean - 讓我們忽略它.
                if (logger.isDebugEnabled()) {
                    logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex);
                }
            }
            // beanType對應的Bean包含@Cotroller或@RequestMapping註解.
            if (beanType != null && isHandler(beanType)) {
                // 從處理器中查詢處理方法,並註冊到mappingRegistry屬性中.
                detectHandlerMethods(beanName);
            }
        }
    }
    //在檢測到所有處理程式方法後呼叫.
    handlerMethodsInitialized(getHandlerMethods());
}

  3) RequestMappingHandlerMapping.isHandler(...)方法。

  ① 給定Bean是否由@Controller@RequestMapping註釋。

/**
 * 判斷給定Bean是否包含@Controller或@RequestMapping.
 */
@Override
protected boolean isHandler(Class<?> beanType) {
    return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
            AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}

  4) AbstractHandlerMethodMapping.detectHandlerMethods()方法。

  ① 獲取處理器handlerBean型別。

  ② 遍歷處理器handler的方法,收集符合條件的方法到methods.

  ③ 遍歷符合條件的方法,將其轉換並註冊到AbstractHandlerMethodMappingmappingRegistry屬性中。

/**
 * 從處理器中查詢處理方法,並註冊到mappingRegistry屬性中.
 * @param handler 處理程式或處理程式例項的Bean名稱.
 */
protected void detectHandlerMethods(final Object handler) {
    // 獲取Bean型別.
    Class<?> handlerType = (handler instanceof String ?
                            obtainApplicationContext().getType((String) handler) : handler.getClass());

    if (handlerType != null) {
        final Class<?> userType = ClassUtils.getUserClass(handlerType);
        // 遍歷Bean類的方法,收集符合條件的methods.
        Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
                                                                  (MethodIntrospector.MetadataLookup<T>) method -> {
                                                                      try {
                                                                          // 獲取對映的方法.
                                                                          return getMappingForMethod(method, userType);
                                                                      }
                                                                      catch (Throwable ex) {
                                                                          throw new IllegalStateException("Invalid mapping on handler class [" +
                                                                                                          userType.getName() + "]: " + method, ex);
                                                                      }
                                                                  });
        if (logger.isDebugEnabled()) {
            logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods);
        }
        // 進行符合條件methods的註冊.
        methods.forEach((method, mapping) -> {
            // 在目標型別上選擇一個呼叫方法.
            Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
            // 註冊HandlerMethod.
            registerHandlerMethod(handler, invocableMethod, mapping);
        });
    }
}

  5) RequestMappingHandlerMapping.getMappingForMethod(...)方法。

  ① 通過createRequestMappingInfo建立方法級別對映。

  ② 通過createRequestMappingInfo建立類級別對映。

  ③ 合併方法級別和類級別createRequestMappingInfo對映。

/**
 * 使用方法級別和型別級別RequestMapping註釋建立RequestMappingInfo.
 * @return 建立的RequestMappingInfo,或者null(如果該方法沒有@RequestMapping註釋).
 */
@Override
@Nullable
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
    // 解析方法級別對映.
    RequestMappingInfo info = createRequestMappingInfo(method);
    if (info != null) {
        // 解析類級別對映.
        RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
        if (typeInfo != null) {
            // 合併方法和類級別對映.
            info = typeInfo.combine(info);
        }
    }
    return info;
}

  6) RequestMappingHandlerMapping.createRequestMappingInfo(...) 方法。

  ① RequestMappingHandlerMapping.createRequestMappingInfo(...) 存在過載方法。

  ② 在處理器方法級別或類級別查詢@RequestMapping註解。

  ③ 通過getCustomTypeCondition(...)載入自定義類級別匹配條件,通過getCustomMethodCondition(...)載入自定義方法級別匹配條件。

  ④ 呼叫createRequestMappingInfo(...)過載方法建立對映。

  ⑤ 綜合@RequestMapping註解指定條件和中自定義條件建立RequestMappingInfo例項。

/**
 * 委託給createRequestMappingInfo(RequestMapping, RequestCondition),
 * 	根據提供的annotatedElement是類或方法來提供適當的自定義RequestCondition.
 */
@Nullable
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
    // 在方法級別或類級別查詢@RequestMapping註解.
    RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
    RequestCondition<?> condition = (element instanceof Class ?
                                     getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
    return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}

/**
 * 從提供的@RequestMapping註解建立RequestMappingInfo,
 * 	它可以是直接宣告的註解、元註解,也可以是在註解層次結構中合併註解屬性的合成結果.
 */
protected RequestMappingInfo createRequestMappingInfo(
    RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {

    RequestMappingInfo.Builder builder = RequestMappingInfo
        .paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))
        .methods(requestMapping.method())
        .params(requestMapping.params())
        .headers(requestMapping.headers())
        .consumes(requestMapping.consumes())
        .produces(requestMapping.produces())
        .mappingName(requestMapping.name());
    if (customCondition != null) {
        builder.customCondition(customCondition);
    }
    return builder.options(this.config).build();
}	

  7) AbstractHandlerMethodMapping.registerHandlerMethod(...)方法。

  主要負責將給定handlermethodmapping註冊到mappingRegistry屬性中。

/**
 * 註冊處理程式方法及其唯一對映.
 * 在啟動時為每個檢測到的處理程式方法呼叫.
 * @param handler 處理程式或處理程式例項的Bean名稱.
 * @param method 註冊的方法.
 * @param mapping 與處理程式方法關聯的對映條件.
 */
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
    this.mappingRegistry.register(mapping, handler, method);
}

  8) AbstractHandlerMethodMapping.handlerMethodsInitialized()方法。

  檢測所有處理程式方法後呼叫。

/**
 * 檢測所有處理程式方法後呼叫.
 */
protected void handlerMethodsInitialized(Map<T, HandlerMethod> handlerMethods) {
}

  總結

  本文對@RequestMapping註冊的處理器的對映解析,瞭解這部分內容有助於更深入的應用@RequestMapping註解。

  原始碼解析基於spring-framework-5.0.5.RELEASE版本原始碼。

  若文中存在錯誤和不足,歡迎指正!