成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

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

imccl / 2639人閱讀

摘要:默認(rèn)支持該策略。以上是對的宏觀分析,下面我們進(jìn)行內(nèi)部細(xì)節(jié)分析。整體流程一通過實(shí)現(xiàn)接口,完成攔截器相關(guān)組件的初始化調(diào)用類的方法??偨Y(jié)本文主要分析了的初始化過程,希望對大家有幫助。隨著學(xué)習(xí)的深入,后面有時(shí)間在分析下期中涉及的關(guān)鍵,比如等等。

概述

本節(jié)我們繼續(xù)分析HandlerMapping另一個(gè)實(shí)現(xiàn)類ReqeustMappingHandlerMapping,該類是我們?nèi)粘i_發(fā)中使用最多的映射器策略,即我們在開發(fā)中使用的注解開發(fā)方式,如:@Controller、@RequestMapping等,都使用的是此映射策略。Spring MVC默認(rèn)支持該策略。

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

類圖

類的繼承關(guān)系,如下圖:

第一次看到此圖可能會感覺好復(fù)雜,大家別急,學(xué)技術(shù)就是這樣,首先需要靜下心,再一個(gè)要學(xué)會對已掌握知識點(diǎn)做總結(jié)、對比、分類,這樣才能把所有的知識點(diǎn)串起來,能系統(tǒng)的了解一項(xiàng)技術(shù)。

以上是我個(gè)人的一些經(jīng)驗(yàn),希望對別人能有幫助,廢話不多說了,我們來分析下這張圖,之前我們學(xué)過了SimpleUrlHandlerMapping和BeanNameUrlHandlerMapping,通過橫向?qū)Ρ龋覀儼l(fā)現(xiàn),他們都繼承自AbstractHandlerMapping抽象類,而AbstractHandlerMapping類的主要工作就是初始化攔截器的功能,三者的實(shí)現(xiàn)都是一樣的。

繼續(xù)分析,我們發(fā)現(xiàn)RequestMappingHandlerMapping增加實(shí)現(xiàn)了InitializingBean和EmbeddedVualeResolverAware接口,即增加了如下能力:

實(shí)現(xiàn)InitializingBean接口,增加了bean初始化的能力,也就是說在bean初始化時(shí)可以做一些控制

實(shí)現(xiàn)EmbeddedValueResolverAware接口,即增加了讀取屬性文件的能力

這兩個(gè)接口都是Spring自動幫我們調(diào)用其中的方法的。也就是說,RequestMappingHandlerMapping通過這些能力來完成初始化HandlerMapping接口的。

以上是對RequestMappingHandlerMapping的宏觀分析,下面我們進(jìn)行內(nèi)部細(xì)節(jié)分析。

整體流程

一、通過實(shí)現(xiàn)ApplicationContextAware接口,完成攔截器相關(guān)組件的初始化

調(diào)用AbstractHandlerMapping類的initApplicationContext()方法。//TODO 下節(jié)分析

二、通過實(shí)現(xiàn)InitializingBean接口,完成url與處理器方法的映射(url->RequestMappingInfo,RequstMappingInfo->HandlerMehtod)

調(diào)用RequestMappingHandlerMapping類實(shí)現(xiàn)的afterPropertiesSet()方法,通過該方法最終調(diào)到其父類的initHandlerMethods()方法,這個(gè)方法是完成映射解析工作:

1、獲取上下文環(huán)境中所有的bean

2、迭代所有的bean,通過isHandler方法判斷是否是handler

2.1 調(diào)用RequestMappingHandlerMapping.isHandler方法,根據(jù)@Controller或@RequestMapping注解判斷(“或”關(guān)系,任意一個(gè))

3、解析出handler中所有需要處理的方法,即標(biāo)注了@RequestMapping注解的方法,封裝在detectHandlerMehtods方法中

3.1 獲取到原始的Class

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

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

3.3 對過濾到的每個(gè)method進(jìn)行注冊,通過registerHandlerMehtod方法完成

通過createHandlerMethod方法創(chuàng)建HandlerMethod對象來封裝處理器方法

判斷匹配條件與處理器是否沖突,即同一個(gè)匹配條件只能對應(yīng)一個(gè)處理器方法

把匹配條件與處理器方法存入map

從匹配條件中解析出url,通過RequestMappingInfoHandlerMapping.getMappingPathPatterns方法實(shí)現(xiàn),之后把url與匹配條件存入另一個(gè)map

4、對HandlerMethod進(jìn)行初始化,調(diào)用handlerMethodsInitialized方法,其內(nèi)部什么都沒做

以上是初始化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去實(shí)現(xiàn)
            if (beanType != null && isHandler(beanType)) {
                // 注冊url與處理器方法的關(guān)系
                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 methods = MethodIntrospector.selectMethods(userType,
                (MethodIntrospector.MetadataLookup) method -> {
                    try {
                        // 調(diào)用子類RequestMappingHandlerMapping的getMappingForMethod方法進(jìn)行處理,即根據(jù)RequestMapping注解信息創(chuàng)建匹配條件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);
            // 調(diào)用本類registerHandlerMethod()方法
            registerHandlerMethod(handler, invocableMethod, mapping);
        });
    }
}

/**
 * 注冊rul和處理器方法的映射關(guān)系
 */
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
    // 調(diào)用內(nèi)部類MappingRegistry的register()方法
    this.mappingRegistry.register(mapping, handler, method);
}

/**
 * 此方法是內(nèi)部類MappingRegistry的方法
 */
public void register(T mapping, Object handler, Method method) {
    this.readWriteLock.writeLock().lock();
    try {
        // 創(chuàng)建處理器方法HandlerMethod實(shí)例,即Controller中的處理方法
        HandlerMethod handlerMethod = createHandlerMethod(handler, method);
        // 判斷匹配條件是否重復(fù),即一個(gè)@RequestMapping的映射url只能對應(yīng)一個(gè)方法
        assertUniqueMethodMapping(handlerMethod, mapping);

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

        // 獲得url映射路徑,將映射路徑和匹配條件對象RequestMappingInfo存起來
        // 調(diào)用本類的getDerectUrls方法
        List 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 getDirectUrls(T mapping) {
    List urls = new ArrayList<>(1);
    // 調(diào)用子類RequestMappingInfoHandlerMapping.getMappingPathPatterns方法
    for (String path : getMappingPathPatterns(mapping)) {
        if (!getPathMatcher().isPattern(path)) {
            urls.add(path);
        }
    }
    return urls;
}

RequestMappingHandlerMapping

根據(jù)@RequestMapping生成RequestMappingInfo對象,主要代碼如下:

/**
 * 使用方法和類型注解@RequestMapping創(chuàng)建RequestMappingInfo對象
 */
@Override
@Nullable
protected RequestMappingInfo getMappingForMethod(Method method, Class handlerType) {
    // 創(chuàng)建方法的RequestMappingInfo
    RequestMappingInfo info = createRequestMappingInfo(method);
    if (info != null) {
        // 創(chuàng)建類的RequestMappingInfo
        RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
        if (typeInfo != null) {
            // 將方法RequestMappingInfo和類RequestMappingInfo合并,比如Controller類上有@RequestMapping("/demo"),方法的@RequestMapping("/demo1"),結(jié)果為"/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));
    // 調(diào)用createRequestMappingInfo創(chuàng)建匹配條件對象
    return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}

/**
 * 構(gòu)造匹配條件對象
 */
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中設(shè)置的value或path
 */
@Override
protected Set getMappingPathPatterns(RequestMappingInfo info) {
    return info.getPatternsCondition().getPatterns();
}

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

總結(jié)

本文主要分析了RequestMappingHandlerMapping的初始化過程,希望對大家有幫助。隨著學(xué)習(xí)的深入,后面有時(shí)間在分析下期中涉及的關(guān)鍵bean,比如:RequestMappingInfo、RequestCondition、HandlerMethod等等。大家加油!

最后創(chuàng)建了qq群方便大家交流,可掃描加入,同時(shí)也可加我qq:276420284,共同學(xué)習(xí)、共同進(jìn)步,謝謝!

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/69487.html

相關(guān)文章

  • SpringMVC源碼分析--HandlerMapping(一)

    摘要:接口接口作用是將請求映射到處理程序,以及預(yù)處理和處理后的攔截器列表,映射是基于一些標(biāo)準(zhǔn)的,其中的細(xì)節(jié)因不同的實(shí)現(xiàn)而不相同。該參數(shù)是類型,作用是檢查所有的映射解析器或使用或?yàn)榈模J(rèn)為,即從上下文中檢查所有的。 概述 在Spring MVC啟動章節(jié)https://segmentfault.com/a/1190000014674239,介紹到了DispatcherServlet的onRef...

    ralap 評論0 收藏0
  • SpringMVC源碼分析--HandlerMapping(五)

    摘要:概述通過前三章的分析,我們簡要分析了和,但對攔截器部分做詳細(xì)的分析,攔截器的加載和初始化是三個(gè)相同的部分。 概述 通過前三章的分析,我們簡要分析了SimpleUrlHandlerMapping、BeanNameUrlHandlerMapping和RequestMappingHandlerMapping,但對攔截器部分做詳細(xì)的分析,攔截器的加載和初始化是三個(gè)HandlerMapping相...

    nanchen2251 評論0 收藏0
  • SpringMVC源碼分析--HandlerMapping(三)

    摘要:與類圖對比,類繼承自抽象類,其又繼承自抽象類,再往上繼承關(guān)系與一致。創(chuàng)建初始化上一章我們分析了的創(chuàng)建初始化過程,的創(chuàng)建初始化過程與一樣,方法的入口在抽象類中的方法。至此,代碼編寫完畢。 概述 本節(jié)我們繼續(xù)分析HandlerMapping另一個(gè)實(shí)現(xiàn)類BeanNameUrlHandlerMapping,從類的名字可知,該類會根據(jù)請求的url與spring容器中定義的bean的name屬性值...

    fsmStudy 評論0 收藏0
  • SpringMVC源碼分析--HandlerMapping(六)

    摘要:概述上一節(jié)我們分析了的初始化過程,即創(chuàng)建并注冊,本章我們分析下的請求處理過程,即查找。本系列文章是基于。最后創(chuàng)建了群方便大家交流,可掃描加入,同時(shí)也可加我,共同學(xué)習(xí)共同進(jìn)步,謝謝 概述 上一節(jié)我們分析了RequestMappingHandlerMapping的初始化過程,即創(chuàng)建并注冊HandlerMehtod,本章我們分析下RequestMappingHandlerMapping的請求...

    BDEEFE 評論0 收藏0
  • SpringMVC源碼分析--HandlerMapping(二)

    摘要:由于抽象類重寫了父類的方法,所以此時(shí)會調(diào)用的方法,在該方法中通過調(diào)用父類的方法,該方法通過模板方法模式最終調(diào)到類的方法。分析該類間接實(shí)現(xiàn)了接口,直接實(shí)現(xiàn)該接口的是抽象類,映射與請求。 概述 在前一章https://segmentfault.com/a/1190000014901736的基礎(chǔ)上繼續(xù)分析,主要完成SimpleUrlHandlerMapping類的原理。 本系列文章是基于Sp...

    Imfan 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<