SpringMVC之源碼分析--HandlerMapping(四)

概述

本節咱們繼續分析HandlerMapping另外一個實現類ReqeustMappingHandlerMapping,該類是咱們平常開發中使用最多的映射器策略,即咱們在開發中使用的註解開發方式,如:@Controller、@RequestMapping等,都使用的是此映射策略。Spring MVC默認支持該策略。app

本系列文章是基於Spring5.0.5RELEASE。cors

類圖

類的繼承關係,以下圖:ide

第一次看到此圖可能會感受好複雜,你們別急,學技術就是這樣,首先須要靜下心,再一個要學會對已掌握知識點作總結、對比、分類,這樣才能把全部的知識點串起來,能系統的瞭解一項技術。源碼分析

以上是我我的的一些經驗,但願對別人能有幫助,廢話很少說了,咱們來分析下這張圖,以前咱們學過了SimpleUrlHandlerMapping和BeanNameUrlHandlerMapping,經過橫向對比,咱們發現,他們都繼承自AbstractHandlerMapping抽象類,而AbstractHandlerMapping類的主要工做就是初始化攔截器的功能,三者的實現都是同樣的。學習

繼續分析,咱們發現RequestMappingHandlerMapping增長實現了InitializingBean和EmbeddedVualeResolverAware接口,即增長了以下能力:ui

  • 實現InitializingBean接口,增長了bean初始化的能力,也就是說在bean初始化時能夠作一些控制
  • 實現EmbeddedValueResolverAware接口,即增長了讀取屬性文件的能力

這兩個接口都是Spring自動幫咱們調用其中的方法的。也就是說,RequestMappingHandlerMapping經過這些能力來完成初始化HandlerMapping接口的。this

以上是對RequestMappingHandlerMapping的宏觀分析,下面咱們進行內部細節分析。url

總體流程

1、經過實現ApplicationContextAware接口,完成攔截器相關組件的初始化spa

  • 調用AbstractHandlerMapping類的initApplicationContext()方法。//TODO 下節分析

2、經過實現InitializingBean接口,完成url與處理器方法的映射(url->RequestMappingInfo,RequstMappingInfo->HandlerMehtod)debug

  • 調用RequestMappingHandlerMapping類實現的afterPropertiesSet()方法,經過該方法最終調到其父類的initHandlerMethods()方法,這個方法是完成映射解析工做:

    一、獲取上下文環境中全部的bean

    二、迭代全部的bean,經過isHandler方法判斷是不是handler

    2.1 調用RequestMappingHandlerMapping.isHandler方法,根據@Controller或@RequestMapping註解判斷(「或」關係,任意一個)

    三、解析出handler中全部須要處理的方法,即標註了@RequestMapping註解的方法,封裝在detectHandlerMehtods方法中

    3.1 獲取到原始的Class<?>

    3.2 使用MethodIntrospector.selectMethods方法過濾具體的handler method,經過模板方法模式getMappingForMethod預留給子類

    • RequestMappingHandlerMapping.getMappingForMehtod方法,經過方法、類上面@RequestMapping註解生成匹配條件RequestMappingInfo對象

    3.3 對過濾到的每一個method進行註冊,經過registerHandlerMehtod方法完成

    • 經過createHandlerMethod方法建立HandlerMethod對象來封裝處理器方法
    • 判斷匹配條件與處理器是否衝突,即同一個匹配條件只能對應一個處理器方法
    • 把匹配條件與處理器方法存入map
    • 從匹配條件中解析出url,經過RequestMappingInfoHandlerMapping.getMappingPathPatterns方法實現,以後把url與匹配條件存入另外一個map

    四、對HandlerMethod進行初始化,調用handlerMethodsInitialized方法,其內部什麼都沒作

以上是初始化RequestMappingHandlerMapping的總體流程。

源碼分析

  • AbstractHandlerMapping

初始化攔截器

// 初始化攔截器,即初始化url須要的攔截器
@Override
protected void initApplicationContext() throws BeansException {
    extendInterceptors(this.interceptors);
    detectMappedInterceptors(this.adaptedInterceptors);
    initInterceptors();
}
  • AbstractHandlerMethodMapping

控制初始化RequestMappingHandlerMapping.initHanderMethods方法總體流程,代碼以下:

/**
 * 在ApplicationContext上下文掃描全部的bean,檢測和註冊處理器方法(handler method)
 */
protected void initHandlerMethods() {
    if (logger.isDebugEnabled()) {
        logger.debug("Looking for request mappings in application context: " + getApplicationContext());
    }
    // 從上下文中查找全部的bean
    String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
            BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
            obtainApplicationContext().getBeanNamesForType(Object.class));
    
    // 遍歷全部的beanName
    for (String beanName : beanNames) {
        if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
            Class<?> beanType = null;
            try {
                // 得到bean的類型
                beanType = obtainApplicationContext().getType(beanName);
            }
            catch (Throwable ex) {
                // An unresolvable bean type, probably from a lazy bean - let's ignore it.
                if (logger.isDebugEnabled()) {
                    logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex);
                }
            }
            // 判斷類是否被@Controller或是@RequestMapping註釋
            // isHandler方法由子類RequestMappingHandlerMapping去實現
            if (beanType != null && isHandler(beanType)) {
                // 註冊url與處理器方法的關係
                detectHandlerMethods(beanName);
            }
        }
    }
    // 空方法
    handlerMethodsInitialized(getHandlerMethods());
}

/**
 * 查找處理程序handler中的處理方法
 */
protected void detectHandlerMethods(final Object handler) {
    // 獲取handler的類型
    Class<?> handlerType = (handler instanceof String ?
            obtainApplicationContext().getType((String) handler) : handler.getClass());

    if (handlerType != null) {
        // 獲取給定類的用戶定義類型,一般爲給定類的類型,但在cglib生成子類的狀況下,返回的是原始類型
        final Class<?> userType = ClassUtils.getUserClass(handlerType);
        // 獲取處理器方法map,key是Method,value是匹配條件RequestMappingInfo對象
        // map中不包括未被@RequestMapping註解的方法
        Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
                (MethodIntrospector.MetadataLookup<T>) method -> {
                    try {
                        // 調用子類RequestMappingHandlerMapping的getMappingForMethod方法進行處理,即根據RequestMapping註解信息建立匹配條件RequestMappingInfo對象
                        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);
        }
        // 遍歷map,得到Method和RequestMappingInfo,註冊他們
        methods.forEach((method, mapping) -> {
            Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
            // 調用本類registerHandlerMethod()方法
            registerHandlerMethod(handler, invocableMethod, mapping);
        });
    }
}

/**
 * 註冊rul和處理器方法的映射關係
 */
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
    // 調用內部類MappingRegistry的register()方法
    this.mappingRegistry.register(mapping, handler, method);
}

/**
 * 此方法是內部類MappingRegistry的方法
 */
public void register(T mapping, Object handler, Method method) {
    this.readWriteLock.writeLock().lock();
    try {
        // 建立處理器方法HandlerMethod實例,即Controller中的處理方法
        HandlerMethod handlerMethod = createHandlerMethod(handler, method);
        // 判斷匹配條件是否重複,即一個@RequestMapping的映射url只能對應一個方法
        assertUniqueMethodMapping(handlerMethod, mapping);

        if (logger.isInfoEnabled()) {
            logger.info("Mapped \"" + mapping + "\" onto " + handlerMethod);
        }
        // 將匹配條件RequestMappingInfo和處理器方法保存到map中
        this.mappingLookup.put(mapping, handlerMethod);

        // 得到url映射路徑,將映射路徑和匹配條件對象RequestMappingInfo存起來
        // 調用本類的getDerectUrls方法
        List<String> directUrls = getDirectUrls(mapping);
        for (String url : directUrls) {
            this.urlLookup.add(url, mapping);
        }

        String name = null;
        if (getNamingStrategy() != null) {
            name = getNamingStrategy().getName(handlerMethod, mapping);
            addMappingName(name, handlerMethod);
        }

        CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
        if (corsConfig != null) {
            this.corsLookup.put(handlerMethod, corsConfig);
        }
        
        // 將映射註冊對象存入map
        this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
    }
    finally {
        this.readWriteLock.writeLock().unlock();
    }
}

private List<String> getDirectUrls(T mapping) {
    List<String> urls = new ArrayList<>(1);
    // 調用子類RequestMappingInfoHandlerMapping.getMappingPathPatterns方法
    for (String path : getMappingPathPatterns(mapping)) {
        if (!getPathMatcher().isPattern(path)) {
            urls.add(path);
        }
    }
    return urls;
}
  • RequestMappingHandlerMapping

根據@RequestMapping生成RequestMappingInfo對象,主要代碼以下:

/**
 * 使用方法和類型註解@RequestMapping建立RequestMappingInfo對象
 */
@Override
@Nullable
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
    // 建立方法的RequestMappingInfo
    RequestMappingInfo info = createRequestMappingInfo(method);
    if (info != null) {
        // 建立類的RequestMappingInfo
        RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
        if (typeInfo != null) {
            // 將方法RequestMappingInfo和類RequestMappingInfo合併,好比Controller類上有@RequestMapping("/demo"),方法的@RequestMapping("/demo1"),結果爲"/demo/demo1"
            info = typeInfo.combine(info);
        }
    }
    return info;
}

@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));
    // 調用createRequestMappingInfo建立匹配條件對象
    return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}

/**
 * 構造匹配條件對象
 */
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();
}
  • RequestMappingInfoHandlerMapping

提供匹配條件RequestMappingInfo的解析處理,涉及的代碼以下:

/**
 * 獲取url集合,即@RequestMapping中設置的value或path
 */
@Override
protected Set<String> getMappingPathPatterns(RequestMappingInfo info) {
    return info.getPatternsCondition().getPatterns();
}

以上即RequestMappingHandlerMapping對象的初始化過程及初始化過程的核心源碼。

總結

本文主要分析了RequestMappingHandlerMapping的初始化過程,但願對你們有幫助。隨着學習的深刻,後面有時間在分析下期中涉及的關鍵bean,好比:RequestMappingInfo、RequestCondition、HandlerMethod等等。你們加油!

最後建立了qq羣方便你們交流,可掃描加入,同時也可加我qq:276420284,共同窗習、共同進步,謝謝!

相關文章
相關標籤/搜索