spring 5.0.x源碼學(xué)習(xí)系列四: AnnotationConfigApplicationContext類register方法作用

前言

上篇博客spring 5.0.x源碼學(xué)習(xí)系列三: AnnotationConfigApplicationContext類的無參構(gòu)造方法的作用介紹了AnnotationConfigApplicationContext類無參構(gòu)造方法的作用, 再次回顧下主要有如下幾個(gè)作用:

  1. 初始化spring bean工廠DefaultListableBeanFactory
  2. 通過AnnotatedBeanDefinitionReader將spring6個(gè)內(nèi)置bean以RootBeanDefinition到bean的類型注冊到工廠, 其中要記住最重要的ConfigurationClassPostProcessor
  3. 初始化ClassPathBeanDefinitionScan(這個(gè)沒啥用, 真正的掃描邏輯并不是用到它)
  4. AnnotationConfigApplicationContext的幾個(gè)身份: BeanDefinitionRegistryGenericApplicationContext

接下來進(jìn)入正文: AnnotationConfigApplicationContext類register方法作用

一艾船、項(xiàng)目demo

在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

一道媚、AnnotationConfigApplicationContext類register方法api

  • 從上之下的源碼就是它的執(zhí)行過程, 附帶注釋
    // AnnotationConfigApplicationContext.java
    /**
     * 顧名思義踊兜,傳入的是被注解的類精刷,并且在里面做了是否存在注解的校驗(yàn)(可以傳多個(gè)類)
     * 根據(jù)代碼可知: 又是通過AnnotatedBeanDefinitionReader來注冊的
     */
    public void register(Class<?>... annotatedClasses) {
        Assert.notEmpty(annotatedClasses, "At least one annotated class must be specified");
        this.reader.register(annotatedClasses);
    }
    // AnnotationConfigApplicationContext.java
    /**
     * 因?yàn)樯鲜鯽pi提供的是一個(gè)可變參數(shù), 所以此處要遍歷注冊它
     */
    public void register(Class<?>... annotatedClasses) {
        for (Class<?> annotatedClass : annotatedClasses) {
            registerBean(annotatedClass);
        }
    }
    // AnnotationConfigApplicationContext.java
    /**
     * 將代碼邏輯委托給另外方法
     */
    public void registerBean(Class<?> annotatedClass) {
        doRegisterBean(annotatedClass, null, null, null);
    }
    // AnnotationConfigApplicationContext.java
    /**
     * 由調(diào)用鏈可知鸵贬,調(diào)用此方法時(shí), 只有第一個(gè)參數(shù)有值,其他的都為null
     * @param annotatedClass
     * @param instanceSupplier
     * @param name
     * @param qualifiers
     * @param definitionCustomizers
     * @param <T>
     */
    <T> void doRegisterBean(Class<T> annotatedClass, @Nullable Supplier<T> instanceSupplier, @Nullable String name,
            @Nullable Class<? extends Annotation>[] qualifiers, BeanDefinitionCustomizer... definitionCustomizers) {

        // 記住這個(gè)創(chuàng)建beanDefinition的api, 很常用诫尽。
        // 在利用spring擴(kuò)展點(diǎn)動(dòng)態(tài)添加一些beanDefinition至bean工廠時(shí)很有用
        AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
        if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
            return;
        }

        // set進(jìn)去的為null, 因?yàn)閭鬟M(jìn)來的為null
        abd.setInstanceSupplier(instanceSupplier);
        ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
        abd.setScope(scopeMetadata.getScopeName());

        // 這塊用的是當(dāng)前類默認(rèn)的beanNameGenerator => AnnotationBeanNameGenerator
        // private BeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();
        // 并不會(huì)使用到我們自定義的beanNameGenerator, 為什么区岗?
        // 因?yàn)槲覀儔焊瓦€沒解析到@ComponentScan注解(這里需要有一點(diǎn)自定義beanNameGenerator的知識(shí)點(diǎn))
        // 大致可以參考我Github中的這個(gè)類:
        // https://github.com/AvengerEug/spring/blob/develop/ioc/src/main/java/com/eugene/sumarry/ioc/annotationtype/MyBeanNameGenerator.java
        String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));

        // 這里開始處理一些通用的注解: 比如@Lazy、@Primary眠菇、@DependsOn辫呻、@Role、@Description
        // 獲取到這些注解中的值, 并填充至傳入的AnnotatedGenericBeanDefinition
        AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);

        // 這塊如果按照spring的流程來基本上用不上, 因?yàn)閭魅氲氖莕ull
        if (qualifiers != null) {
            for (Class<? extends Annotation> qualifier : qualifiers) {
                if (Primary.class == qualifier) {
                    abd.setPrimary(true);
                }
                else if (Lazy.class == qualifier) {
                    abd.setLazyInit(true);
                }
                else {
                    abd.addQualifier(new AutowireCandidateQualifier(qualifier));
                }
            }
        }

        // 傳入的也為null, 按照正常流程來, 先忽略它
        for (BeanDefinitionCustomizer customizer : definitionCustomizers) {
            customizer.customize(abd);
        }

        // 不知道是為了啥, 要new這么一個(gè)對象, 可能只是為了方便傳值吧 - -琼锋!
        BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);

        // 這塊暫時(shí)不知道放闺。。缕坎。
        definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);

        // 注冊beanDefinition, 傳入的是registry, 有了上篇博客的基礎(chǔ)怖侦,
        // 易知該registry就是AnnotationConfigApplicationContext
        BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
    }

    // BeanDefinitionReaderUtils.java
    /**
     * 注冊beanDefinition
     */
    public static void registerBeanDefinition(
            BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
            throws BeanDefinitionStoreException {

        // Register bean definition under primary name.
        String beanName = definitionHolder.getBeanName();
        
        // 它前面封裝了一個(gè)definitionHolder, 現(xiàn)在又拆解它......
        // 該register是一個(gè)AnnotationConfigApplicationContext
        // 但此時(shí)是調(diào)用父類GenericApplicationContext的registerBeanDefinition方法
        registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

        // Register aliases for bean name, if any.
        String[] aliases = definitionHolder.getAliases();
        if (aliases != null) {
            for (String alias : aliases) {
                registry.registerAlias(beanName, alias);
            }
        }
    }

    // DefaultListableBeanFactory.java
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
            throws BeanDefinitionStoreException {

        Assert.hasText(beanName, "Bean name must not be empty");
        Assert.notNull(beanDefinition, "BeanDefinition must not be null");

        // 這里會(huì)校驗(yàn)當(dāng)前的beanDefinition, 具體校驗(yàn)啥沒具體看,
        // 但beanDefinition instanceof AbstractBeanDefinition條件是成立的
        // 因?yàn)樗?AnnotatedGenericBeanDefinition)繼承了AbstractBeanDefinition
        if (beanDefinition instanceof AbstractBeanDefinition) {
            try {
                ((AbstractBeanDefinition) beanDefinition).validate();
            }
            catch (BeanDefinitionValidationException ex) {
                throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                        "Validation of bean definition failed", ex);
            }
        }

        // beanDefinitionMap: 這個(gè)屬性是bean工廠存放定義的beanDefinition
        // 因?yàn)橐詁eanDefinition, 所以先校驗(yàn)它是否存在, 正常流程中
        // 這里基本為null
        BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
        if (existingDefinition != null) {
            if (!isAllowBeanDefinitionOverriding()) {
                throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                        "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
                        "': There is already [" + existingDefinition + "] bound.");
            }
            else if (existingDefinition.getRole() < beanDefinition.getRole()) {
                // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
                if (logger.isWarnEnabled()) {
                    logger.warn("Overriding user-defined bean definition for bean '" + beanName +
                            "' with a framework-generated bean definition: replacing [" +
                            existingDefinition + "] with [" + beanDefinition + "]");
                }
            }
            else if (!beanDefinition.equals(existingDefinition)) {
                if (logger.isInfoEnabled()) {
                    logger.info("Overriding bean definition for bean '" + beanName +
                            "' with a different definition: replacing [" + existingDefinition +
                            "] with [" + beanDefinition + "]");
                }
            }
            else {
                if (logger.isDebugEnabled()) {
                    logger.debug("Overriding bean definition for bean '" + beanName +
                            "' with an equivalent definition: replacing [" + existingDefinition +
                            "] with [" + beanDefinition + "]");
                }
            }
            this.beanDefinitionMap.put(beanName, beanDefinition);
        }
        else {
            // 判斷bean是否在創(chuàng)建, 正常的register流程中, 返回的基本為false
            if (hasBeanCreationStarted()) {
                // Cannot modify startup-time collection elements anymore (for stable iteration)
                synchronized (this.beanDefinitionMap) {
                    this.beanDefinitionMap.put(beanName, beanDefinition);
                    List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
                    updatedDefinitions.addAll(this.beanDefinitionNames);
                    updatedDefinitions.add(beanName);
                    this.beanDefinitionNames = updatedDefinitions;
                    if (this.manualSingletonNames.contains(beanName)) {
                        Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
                        updatedSingletons.remove(beanName);
                        this.manualSingletonNames = updatedSingletons;
                    }
                }
            }
            else {
                // 將beanD注冊到bean工廠中,
                // 1. 添加到beanDefinitionMap
                // 2. 添加到beanDefinitionNames
                // 3. 從manualSingletonNames中移除, 這里不清楚manualSingletonNames
                // 屬性是干啥的, 不過根據(jù)名字來猜測: 存放手動(dòng)創(chuàng)建的單例bean的名字谜叹?
                this.beanDefinitionMap.put(beanName, beanDefinition);
                this.beanDefinitionNames.add(beanName);
                this.manualSingletonNames.remove(beanName);
            }
            this.frozenBeanDefinitionNames = null;
        }

        if (existingDefinition != null || containsSingleton(beanName)) {
            resetBeanDefinition(beanName);
        }
    }

二匾寝、運(yùn)行結(jié)果

在這里插入圖片描述

在這里插入圖片描述

三、小結(jié)

  • Q1: 上面的代碼執(zhí)行完后(register方法執(zhí)行完畢)荷腊,往bean工廠中注冊了幾個(gè)beanDefinition, bean工廠中一共有幾個(gè)beanDefinition艳悔?
  • A1: 注冊了2個(gè)beanDefinition, 此時(shí)bean工廠中一共有 6 + 2 = 8 個(gè)beanDefinition。

  • Q2: 現(xiàn)在bean工廠中存在的beanDefinition的類型總共有幾種女仰?分別是猜年?
  • A2: 兩種。 分別是RootBeanDefinitionAnnotatedGenericBeanDefinition 疾忍。

  • Q3: 現(xiàn)在有bean被創(chuàng)建出來嗎乔外?
  • A3: 沒有, register方法只是注冊beanDefinition。 一般是注冊一個(gè)配置類(eg: 包含@ComponentScan注解的類)方便spring的后續(xù)操作(eg: 掃描包解析注解等等)一罩。

  • Q4: spring認(rèn)為描述bean的通用注解有哪些杨幼?
  • A4: @Lazy、@Primary聂渊、@DependsOn差购、@Role、@Description

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末汉嗽,一起剝皮案震驚了整個(gè)濱河市欲逃,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌诊胞,老刑警劉巖暖夭,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件锹杈,死亡現(xiàn)場離奇詭異,居然都是意外死亡迈着,警方通過查閱死者的電腦和手機(jī)竭望,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來裕菠,“玉大人咬清,你說我怎么就攤上這事∨耍” “怎么了旧烧?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長画髓。 經(jīng)常有香客問我掘剪,道長,這世上最難降的妖魔是什么奈虾? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任夺谁,我火速辦了婚禮,結(jié)果婚禮上肉微,老公的妹妹穿的比我還像新娘匾鸥。我一直安慰自己,他們只是感情好碉纳,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布勿负。 她就那樣靜靜地躺著,像睡著了一般劳曹。 火紅的嫁衣襯著肌膚如雪奴愉。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天厚者,我揣著相機(jī)與錄音躁劣,去河邊找鬼。 笑死库菲,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的志膀。 我是一名探鬼主播熙宇,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼溉浙!你這毒婦竟也來了烫止?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤戳稽,失蹤者是張志新(化名)和其女友劉穎馆蠕,沒想到半個(gè)月后期升,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡互躬,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年播赁,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片吼渡。...
    茶點(diǎn)故事閱讀 39,902評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡容为,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出寺酪,到底是詐尸還是另有隱情坎背,我是刑警寧澤,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布寄雀,位于F島的核電站得滤,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏盒犹。R本人自食惡果不足惜懂更,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望阿趁。 院中可真熱鬧膜蛔,春花似錦、人聲如沸脖阵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽命黔。三九已至呜呐,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間悍募,已是汗流浹背蘑辑。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留坠宴,地道東北人洋魂。 一個(gè)月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像喜鼓,于是被迫代替她去往敵國和親副砍。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評論 2 354

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