Spring源碼分析——AnnotationConfigApplicationContext組件注冊(cè)流程

工程搭建

Maven依賴:

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.5</version>
    </dependency>
</dependencies>

在項(xiàng)目下新建一個(gè)byx.test包,然后在里面添加A踢械、BConfig三個(gè)類:

public class A {
}

public class B {
}

@Component
public class Config {
}

AB是兩個(gè)普通的類(沒有標(biāo)注Component)注解,Config標(biāo)注了Component注解坐搔,所以理論上只有Config會(huì)被注冊(cè)到容器中。

然后再添加一個(gè)Main類作為啟動(dòng)類:

public class Main {
    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext("byx.test");
        // 輸出容器中的所有bean的name
        for (String name : ctx.getBeanDefinitionNames()) {
            System.out.println(name);
        }
    }
}

main函數(shù)中敬矩,創(chuàng)建了一個(gè)AnnotationConfigApplicationContext概行,然后輸出容器中所有bean的name。

最終的項(xiàng)目結(jié)構(gòu)是這樣的:

1.png

運(yùn)行Main弧岳,控制臺(tái)輸出如下:

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
config

可以看到凳忙,容器中一共有5個(gè)bean业踏,其中四個(gè)帶internal的都是Spring內(nèi)部自帶的,config則是我們之前定義的Config類涧卵,下面就來探究一下這些組件是如何被注冊(cè)的堡称。

AnnotationConfigApplicationContext組件注冊(cè)流程

main函數(shù)的這一行加一個(gè)斷點(diǎn),并啟動(dòng)調(diào)試:

2.png

首先step into艺演,然后多次step over却紧,直到進(jìn)入AnnotationConfigApplicationContext的構(gòu)造函數(shù):

3.png

繼續(xù)step over,執(zhí)行完this()調(diào)用:

4.png

這里分享一個(gè)調(diào)試Spring的小技巧胎撤,就是通過觀察BeanFactory內(nèi)部的beanDefinitionMap這個(gè)成員變量來分析組件注冊(cè)的時(shí)機(jī)晓殊。beanDefinitionMap是一個(gè)ConcurrentHashMap,它的鍵是bean的name,值是對(duì)應(yīng)的BeanDefinition么鹤。通過觀察這個(gè)變量筑辨,我們就可以知道當(dāng)前容器中所有已注冊(cè)的bean信息。

現(xiàn)在把注意力放在調(diào)試器的Varieables面板介汹,找到this.beanFactory.beanDefinitionMap這個(gè)變量。

5.png

可以看到舶沛,beanDefinitionMap的大小為4嘹承,里面已經(jīng)有了四個(gè)bean:

這四個(gè)bean都是Spring內(nèi)部自帶的組件,由此可推測(cè)如庭,Spring內(nèi)部自帶的組件的注冊(cè)是在this()調(diào)用中叹卷,即AnnotationConfigApplicationContext的默認(rèn)構(gòu)造函數(shù)中完成的,坪它。

繼續(xù)step over骤竹,執(zhí)行完scan(basePackages)這行后,發(fā)現(xiàn)beanDefinitionMap的大小變成了5往毡,增加了一個(gè)name為config的bean蒙揣,正是我們自定義的Config類(該類被Component注解標(biāo)注):

6.png
7.png

由此可推測(cè),Component注解標(biāo)注的類是在scan(basePackages)調(diào)用中被注冊(cè)的开瞭。從方法名可以推測(cè)懒震,其內(nèi)部執(zhí)行了一個(gè)包掃描的操作。

Spring內(nèi)置組件的注冊(cè)

回到AnnotationConfigApplicationContext的構(gòu)造函數(shù):

public AnnotationConfigApplicationContext(String... basePackages) {
    this();
    scan(basePackages);
    refresh();
}

從上面的分析可以知道惩阶,AnnotationConfigApplicationContext在它的默認(rèn)構(gòu)造函數(shù)中注冊(cè)內(nèi)部組件挎狸,即this()調(diào)用,實(shí)現(xiàn)如下:

public AnnotationConfigApplicationContext() {
    StartupStep createAnnotatedBeanDefReader = this.getApplicationStartup().start("spring.context.annotated-bean-reader.create");
    this.reader = new AnnotatedBeanDefinitionReader(this);
    createAnnotatedBeanDefReader.end();
    this.scanner = new ClassPathBeanDefinitionScanner(this);
}

使用調(diào)試器跟蹤断楷,同時(shí)注意beanDefinitionMap的變化锨匆,發(fā)現(xiàn)注冊(cè)操作發(fā)生在this.reader = new AnnotatedBeanDefinitionReader(this)這行代碼中,所以直接查看AnnotatedBeanDefinitionReader的構(gòu)造函數(shù):

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
    this(registry, getOrCreateEnvironment(registry));
}

繼續(xù)進(jìn)入另一個(gè)構(gòu)造函數(shù):

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    Assert.notNull(environment, "Environment must not be null");
    this.registry = registry;
    this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
    AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}

真正的注冊(cè)操作發(fā)生在AnnotationConfigUtilsregisterAnnotationConfigProcessors方法中:

public static void registerAnnotationConfigProcessors(BeanDefinitionRegistry registry) {
    registerAnnotationConfigProcessors(registry, null);
}

繼續(xù)進(jìn)入registerAnnotationConfigProcessors重載方法,終于看到了核心代碼(省略了一部分無關(guān)緊要的內(nèi)容):

public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
        BeanDefinitionRegistry registry, @Nullable Object source) {
    ...
    Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);

    // 注冊(cè)org.springframework.context.annotation.internalConfigurationAnnotationProcessor
    // ConfigurationClassPostProcessor用來處理Configuration注解
    if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
        def.setSource(source);
        beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
    }

    // 注冊(cè)org.springframework.context.annotation.internalAutowiredAnnotationProcessor
    // AutowiredAnnotationBeanPostProcessor用來處理Autowired注解
    if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
        def.setSource(source);
        beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
    }
    ...
    // 注冊(cè)org.springframework.context.event.internalEventListenerProcessor
    // 與EventListener有關(guān)
    if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
        def.setSource(source);
        beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
    }

    // 注冊(cè)org.springframework.context.event.internalEventListenerFactory
    // 還是與EventListener有關(guān)
    if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
        def.setSource(source);
        beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
    }

    return beanDefs;
}

registerAnnotationConfigProcessors方法內(nèi)部注冊(cè)了我們?cè)诳刂婆_(tái)輸出中看到的四個(gè)Spring內(nèi)置組件恐锣。

Component注解的處理

回到AnnotationConfigApplicationContext的構(gòu)造函數(shù):

public AnnotationConfigApplicationContext(String... basePackages) {
    this();
    scan(basePackages);
    refresh();
}

從上面的分析可以知道茅主,scan(basePackages)這個(gè)調(diào)用負(fù)責(zé)掃描并注冊(cè)被Component標(biāo)注的bean,該方法的實(shí)現(xiàn)如下:

public void scan(String... basePackages) {
    Assert.notEmpty(basePackages, "At least one base package must be specified");
    StartupStep scanPackages = this.getApplicationStartup().start("spring.context.base-packages.scan")
            .tag("packages", () -> Arrays.toString(basePackages));
    this.scanner.scan(basePackages);
    scanPackages.end();
}

真正干活的是this.scanner.scan(basePackages)這個(gè)調(diào)用土榴,其中this.scanner是一個(gè)ClassPathBeanDefinitionScanner的實(shí)例诀姚,它在AnnotationConfigApplicationContext的默認(rèn)構(gòu)造函數(shù)中被初始化:

public AnnotationConfigApplicationContext() {
    StartupStep createAnnotatedBeanDefReader = this.getApplicationStartup().start("spring.context.annotated-bean-reader.create");
    this.reader = new AnnotatedBeanDefinitionReader(this);
    createAnnotatedBeanDefReader.end();
    this.scanner = new ClassPathBeanDefinitionScanner(this); // 初始化scanner
}

ClassPathBeanDefinitionScannerscan方法實(shí)現(xiàn)如下:

public int scan(String... basePackages) {
    int beanCountAtScanStart = this.registry.getBeanDefinitionCount();

    doScan(basePackages);

    // Register annotation config processors, if necessary.
    if (this.includeAnnotationConfig) {
        AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
    }

    return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
}

scan方法內(nèi)部調(diào)用了doScan方法,同時(shí)還記錄了bean數(shù)量的改變量玷禽。doScan方法實(shí)現(xiàn)如下:

// basePackages就是我們?cè)趍ain函數(shù)中構(gòu)造AnnotationConfigApplicationContext時(shí)傳入的包名
// 從這里也可以看出赫段,我們可以同時(shí)傳入多個(gè)包名
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
    Assert.notEmpty(basePackages, "At least one base package must be specified");
    Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
    // 遍歷每個(gè)包名
    for (String basePackage : basePackages) {
        // 尋找每個(gè)包下符合條件的類,并包裝成BeanDefinition
        Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
        // 遍歷找到的每個(gè)BeanDefinition
        for (BeanDefinition candidate : candidates) {
            // 設(shè)置scope屬性
            ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
            candidate.setScope(scopeMetadata.getScopeName());
            // 生成beanName
            String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
            // 設(shè)置一些默認(rèn)屬性
            if (candidate instanceof AbstractBeanDefinition) {
                postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
            }
            // 處理Lazy矢赁、Primary糯笙、DependsOn、Role撩银、Description這些注解
            if (candidate instanceof AnnotatedBeanDefinition) {
                AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
            }
            // 把BeanDefinition包裝成BeanDefinitionHolder
            if (checkCandidate(beanName, candidate)) {
                BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
                definitionHolder =
                        AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
                beanDefinitions.add(definitionHolder);
                // 真正執(zhí)行注冊(cè)操作
                registerBeanDefinition(definitionHolder, this.registry);
            }
        }
    }
    // 返回所有BeanDefinitionHolder
    return beanDefinitions;
}

doScan方法是注冊(cè)bean的核心邏輯给涕,它遍歷每個(gè)傳入的包名,通過調(diào)用findCandidateComponents方法來獲取每個(gè)包下滿足條件的bean额获,然后進(jìn)行一些必要的設(shè)置够庙,最后調(diào)用registerBeanDefinition方法完成注冊(cè)操作。

findCandidateComponents方法的實(shí)現(xiàn)如下:

public Set<BeanDefinition> findCandidateComponents(String basePackage) {
    if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
        return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
    }
    else {
        return scanCandidateComponents(basePackage);
    }
}

scanCandidateComponents方法的實(shí)現(xiàn)如下:

private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
    Set<BeanDefinition> candidates = new LinkedHashSet<>();
    try {
        // 將包名轉(zhuǎn)換成一個(gè)資源url
        String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                resolveBasePackage(basePackage) + '/' + this.resourcePattern;
        // 讀取資源
        Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
        boolean traceEnabled = logger.isTraceEnabled();
        boolean debugEnabled = logger.isDebugEnabled();
        // 遍歷所有資源抄邀,每個(gè)資源表示一個(gè).class文件
        for (Resource resource : resources) {
            if (traceEnabled) {
                logger.trace("Scanning " + resource);
            }
            if (resource.isReadable()) {
                try {
                    // 獲取class的元數(shù)據(jù)耘眨,包括注解的信息
                    MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
                    // 判斷是否滿足條件
                    if (isCandidateComponent(metadataReader)) {
                        // 構(gòu)造BeanDefinition
                        ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                        sbd.setSource(resource);
                        if (isCandidateComponent(sbd)) {
                            if (debugEnabled) {
                                logger.debug("Identified candidate component class: " + resource);
                            }
                            candidates.add(sbd);
                        }
                        else {
                            if (debugEnabled) {
                                logger.debug("Ignored because not a concrete top-level class: " + resource);
                            }
                        }
                    }
                    else {
                        if (traceEnabled) {
                            logger.trace("Ignored because not matching any filter: " + resource);
                        }
                    }
                }
                catch (Throwable ex) {
                    throw new BeanDefinitionStoreException(
                            "Failed to read candidate component class: " + resource, ex);
                }
            }
            else {
                if (traceEnabled) {
                    logger.trace("Ignored because not readable: " + resource);
                }
            }
        }
    }
    catch (IOException ex) {
        throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
    }
    // 返回所有滿足條件的BeanDefinition
    return candidates;
}

scanCandidateComponents方法使用Spring內(nèi)置的資源讀取機(jī)制讀取指定包下的所有class文件,然后轉(zhuǎn)換成MetadataReader撤摸,并傳入isCandidateComponent方法判斷是否滿足要求毅桃,如果滿足要求則加入isCandidateComponent集合。

isCandidateComponent方法的實(shí)現(xiàn)如下:

protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
    for (TypeFilter tf : this.excludeFilters) {
        if (tf.match(metadataReader, getMetadataReaderFactory())) {
            return false;
        }
    }
    for (TypeFilter tf : this.includeFilters) {
        if (tf.match(metadataReader, getMetadataReaderFactory())) {
            return isConditionMatch(metadataReader);
        }
    }
    return false;
}

isCandidateComponent方法通過excludeFiltersincludeFilters兩個(gè)集合來對(duì)MetadataReader進(jìn)行過濾准夷。在調(diào)試中可以發(fā)現(xiàn),includeFilters包含了一個(gè)Component注解的過濾器莺掠,所以可以過濾出標(biāo)注了Component的類衫嵌。

如果使用調(diào)試器調(diào)試程序,可以發(fā)現(xiàn)彻秆,isCandidateComponent方法只會(huì)對(duì)Config類返回true楔绞,而對(duì)其他類(AB唇兑、Main)都返回false酒朵。

includeFilters的初始化是在org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#registerDefaultFilters方法中被初始化的(ClassPathScanningCandidateComponentProviderClassPathBeanDefinitionScanner的父類):

protected void registerDefaultFilters() {
    this.includeFilters.add(new AnnotationTypeFilter(Component.class));
    ...
}

到此,Component注解的處理過程就分析完了扎附。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蔫耽,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子留夜,更是在濱河造成了極大的恐慌匙铡,老刑警劉巖图甜,帶你破解...
    沈念sama閱讀 219,490評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異鳖眼,居然都是意外死亡黑毅,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門钦讳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來矿瘦,“玉大人,你說我怎么就攤上這事愿卒》朔玻” “怎么了?”我有些...
    開封第一講書人閱讀 165,830評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵掘猿,是天一觀的道長(zhǎng)病游。 經(jīng)常有香客問我,道長(zhǎng)稠通,這世上最難降的妖魔是什么衬衬? 我笑而不...
    開封第一講書人閱讀 58,957評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮改橘,結(jié)果婚禮上滋尉,老公的妹妹穿的比我還像新娘。我一直安慰自己飞主,他們只是感情好狮惜,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評(píng)論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著碌识,像睡著了一般碾篡。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上筏餐,一...
    開封第一講書人閱讀 51,754評(píng)論 1 307
  • 那天开泽,我揣著相機(jī)與錄音,去河邊找鬼魁瞪。 笑死穆律,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的导俘。 我是一名探鬼主播峦耘,決...
    沈念sama閱讀 40,464評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼旅薄!你這毒婦竟也來了辅髓?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎利朵,沒想到半個(gè)月后律想,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,847評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡绍弟,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評(píng)論 3 338
  • 正文 我和宋清朗相戀三年技即,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片樟遣。...
    茶點(diǎn)故事閱讀 40,137評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡而叼,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出豹悬,到底是詐尸還是另有隱情葵陵,我是刑警寧澤,帶...
    沈念sama閱讀 35,819評(píng)論 5 346
  • 正文 年R本政府宣布瞻佛,位于F島的核電站脱篙,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏伤柄。R本人自食惡果不足惜绊困,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望适刀。 院中可真熱鬧秤朗,春花似錦、人聲如沸笔喉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽常挚。三九已至作谭,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間待侵,已是汗流浹背丢早。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留秧倾,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,409評(píng)論 3 373
  • 正文 我出身青樓傀缩,卻偏偏與公主長(zhǎng)得像那先,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子赡艰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容