18--Spring注冊(cè)BeanDefinition

上一節(jié)分析了Spring解析xml默認(rèn)命名空間并將解析到的數(shù)據(jù)封住至BeanDefinitionHolder對(duì)象中。接下來(lái)就可以執(zhí)行BeanDefinition的注冊(cè)了闷愤。該過(guò)程分為兩個(gè)部分,注冊(cè)BeanDefinition和注冊(cè)aliases(別名)何暇。

1. 引

上一節(jié)分析到了processBeanDefinition方法中的第一步句柠,接著往下執(zhí)行就應(yīng)該是裝飾BeanDefinition對(duì)象和執(zhí)行BeanDefinition注冊(cè)。

/**
 * 解析bean標(biāo)簽將其轉(zhuǎn)換為definition并注冊(cè)到BeanDefinitionRegistry
 * Process the given bean element, parsing the bean definition and registering it with the registry.
 */
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
    // 1桃序、將解析的節(jié)點(diǎn)信息封裝至BeanDefinitionHolder對(duì)象
    // BeanDefinitionHolder-->封裝了BeanDefinition,beanName以及aliases
    BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
    if (bdHolder != null) {
        // 2杖虾、裝飾BeanDefinition
        bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
        try {
            // Register the final decorated instance.
            // 3、執(zhí)行注冊(cè)
            BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
        }
        catch (BeanDefinitionStoreException ex) {
            getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, ex);
        }
        // Send registration event.
        // 4媒熊、發(fā)送注冊(cè)事件
        getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
    }
}
1. 注冊(cè)BeanDefinition步驟簡(jiǎn)析
public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException {

    // Register bean definition under primary name.
    // 1奇适、注冊(cè)BeanDefinition
    String beanName = definitionHolder.getBeanName();
    registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

    // Register aliases for bean name, if any.
    // 2、注冊(cè)aliases(別名)
    String[] aliases = definitionHolder.getAliases();
    if (aliases != null) {
        for (String alias : aliases) {
            registry.registerAlias(beanName, alias);
        }
    }
}

從代碼中可以了解到芦鳍,該過(guò)程一共分為了兩個(gè)部分

  • 1嚷往、注冊(cè)BeanDefinition
  • 2、注冊(cè)aliases(別名)

下面對(duì)這兩個(gè)過(guò)程逐一進(jìn)行分析柠衅。

3.注冊(cè)注冊(cè)BeanDefinition

注冊(cè)注冊(cè)BeanDefinition的工作交給了BeanDefinitionRegistry接口的默認(rèn)實(shí)現(xiàn)DefaultListableBeanFactory皮仁。該過(guò)程還是比較復(fù)雜的。來(lái)看代碼菲宴。贷祈。。

@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException {

    // 1喝峦、如果beanDefinition是AbstractBeanDefinition實(shí)例,則驗(yàn)證
    if (beanDefinition instanceof AbstractBeanDefinition) {
        try {
            //驗(yàn)證不能將靜態(tài)工廠方法與方法重寫(xiě)相結(jié)合(靜態(tài)工廠方法必須創(chuàng)建實(shí)例)
            ((AbstractBeanDefinition) beanDefinition).validate();
        }
        catch (BeanDefinitionValidationException ex) {
            throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", ex);
        }
    }

    // 2势誊、優(yōu)先嘗試從緩存中加載BeanDefinition
    BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
    if (existingDefinition != null) {
        // beanName已經(jīng)存在且不允許被覆蓋,拋出異常
        if (!isAllowBeanDefinitionOverriding()) {
            throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
        }
        // 使用新的BeanDefinition覆蓋已經(jīng)加載的BeanDefinition谣蠢,if else中只有日志打印键科,無(wú)實(shí)質(zhì)代碼闻丑,刪除為了閱讀方便
        else if (existingDefinition.getRole() < beanDefinition.getRole()) {}
        else if (!beanDefinition.equals(existingDefinition)) {}
        else {}
        this.beanDefinitionMap.put(beanName, beanDefinition);
    }
    // 3、緩存中無(wú)對(duì)應(yīng)的BeanDefinition勋颖,則直接注冊(cè)
    else {
        // 如果beanDefinition已經(jīng)被標(biāo)記為創(chuàng)建(為了解決單例bean的循環(huán)依賴(lài)問(wèn)題)
        if (hasBeanCreationStarted()) {
            // Cannot modify startup-time collection elements anymore (for stable iteration)
            synchronized (this.beanDefinitionMap) {
                // 加入beanDefinitionMap
                this.beanDefinitionMap.put(beanName, beanDefinition);
                // 創(chuàng)建List<String>并將緩存的beanDefinitionNames和新解析的beanName加入集合
                List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
                updatedDefinitions.addAll(this.beanDefinitionNames);
                updatedDefinitions.add(beanName);
                // 將updatedDefinitions賦值給beanDefinitionNames
                this.beanDefinitionNames = updatedDefinitions;
                // 如果manualSingletonNames中包含新注冊(cè)的beanName
                if (this.manualSingletonNames.contains(beanName)) {
                    // 創(chuàng)建set集合并將manualSingletonNames加入到新創(chuàng)建的set集合
                    Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
                    // 移除新注冊(cè)的beanName
                    updatedSingletons.remove(beanName);
                    this.manualSingletonNames = updatedSingletons;
                }
            }
            //以上操作就是重新創(chuàng)建beanDefinitionNames和manualSingletonNames集合,這段代碼我也不是很理解...
        }
        else {
            // 將beanDefinition信息維護(hù)至緩存
            // beanDefinitionMap-->(key->beanName,value->beanDefinition)
            this.beanDefinitionMap.put(beanName, beanDefinition);
            // beanDefinitionNames-->維護(hù)了beanName集合
            this.beanDefinitionNames.add(beanName);
            // manualSingletonNames緩存了手動(dòng)注冊(cè)的單例bean嗦嗡,所以需要調(diào)用一下remove方法,防止beanName重復(fù)
            // 例如:xmlBeanFactory.registerSingleton("myDog", new Dog());
            // 就可以向manualSingletonNames中注冊(cè)單例bean
            this.manualSingletonNames.remove(beanName);
        }
        this.frozenBeanDefinitionNames = null;
    }

    // 4饭玲、重置BeanDefinition侥祭,
    // 當(dāng)前注冊(cè)的bean的定義已經(jīng)在beanDefinitionMap緩存中存在,
    // 或者其實(shí)例已經(jīng)存在于單例bean的緩存中
    if (existingDefinition != null || containsSingleton(beanName)) {
        resetBeanDefinition(beanName);
    }
}

注冊(cè)流程在代碼里已經(jīng)標(biāo)注的很清楚了茄厘,其中synchronized方法中的代碼矮冬,分析不是很明確,希望了解的同學(xué)可以留言指正次哈。

4.注冊(cè)別名
// 注冊(cè)別名
@Override
public void registerAlias(String name, String alias) {
    Assert.hasText(name, "'name' must not be empty");
    Assert.hasText(alias, "'alias' must not be empty");
    synchronized (this.aliasMap) {
        // 如果beanName與別名相同
        if (alias.equals(name)) {
            //移除別名
            this.aliasMap.remove(alias);
            if (logger.isDebugEnabled()) {
                logger.debug("Alias definition '" + alias + "' ignored since it points to same name");
            }
        }
        else {
            //嘗試從緩存中獲取別名
            String registeredName = this.aliasMap.get(alias);
            //如果別名已經(jīng)在緩存中存在
            if (registeredName != null) {
                //緩存中的別名和beanName(注意:不是別名)相同,不做任何操作,不需要再次注冊(cè)
                if (registeredName.equals(name)) {
                    // An existing alias - no need to re-register
                    return;
                }
                //緩存中存在別名,且不允許覆蓋,拋出異常
                if (!allowAliasOverriding()) {
                    throw new IllegalStateException("Cannot define alias '" + alias + "' for name '" +
                            name + "': It is already registered for name '" + registeredName + "'.");
                }
            }
            //檢查給定名稱(chēng)是否已指向另一個(gè)方向的別名作為別名,預(yù)先捕獲循環(huán)引用并拋出相應(yīng)的IllegalStateException
            checkForAliasCircle(name, alias);
            //緩存別名
            this.aliasMap.put(alias, name);
        }
    }
}
5.分析

以上操作已經(jīng)完成了整個(gè)BeanDefinition和別名的注冊(cè)胎署,加上以前分析的代碼我們可以發(fā)現(xiàn)一些Spring源碼中的書(shū)寫(xiě)技巧。

  • 方法職責(zé)單一窑滞,Spring中很少有一個(gè)長(zhǎng)而復(fù)雜的方法琼牧,也很少在一個(gè)方法里處理多個(gè)邏輯,即便有也是將具體的邏輯分支細(xì)化哀卫,在主方法里調(diào)用巨坊。
  • 處理流程明確簡(jiǎn)析,Spring中很多方法此改,例如我們今天分析的BeanDefinition注冊(cè)趾撵,以及接下來(lái)要分析的獲取bean的過(guò)程等,都是先進(jìn)行合法驗(yàn)證共啃、嘗試從緩存中獲取占调、如未能從緩存中獲取到才去真正創(chuàng)建對(duì)象實(shí)例、將新創(chuàng)建的實(shí)例加入緩存等等移剪。

以上只是兩個(gè)小例子妈候,多讀代碼,多跟蹤調(diào)試挂滓,才是閱讀開(kāi)源代碼的途徑苦银,隨著閱讀次數(shù)的增加,相信大家會(huì)原來(lái)越加深對(duì)框架的理解赶站,當(dāng)我們真正能讀懂Spring的源碼之后幔虏,相信大家在看其他的源碼,也能觸類(lèi)旁通贝椿,成為閱讀開(kāi)源代碼的高手想括。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市烙博,隨后出現(xiàn)的幾起案子瑟蜈,更是在濱河造成了極大的恐慌烟逊,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,482評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件铺根,死亡現(xiàn)場(chǎng)離奇詭異宪躯,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)位迂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)访雪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人掂林,你說(shuō)我怎么就攤上這事臣缀。” “怎么了泻帮?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,762評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵精置,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我锣杂,道長(zhǎng)脂倦,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,273評(píng)論 1 279
  • 正文 為了忘掉前任蹲堂,我火速辦了婚禮狼讨,結(jié)果婚禮上贝淤,老公的妹妹穿的比我還像新娘柒竞。我一直安慰自己,他們只是感情好播聪,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評(píng)論 5 373
  • 文/花漫 我一把揭開(kāi)白布朽基。 她就那樣靜靜地躺著,像睡著了一般离陶。 火紅的嫁衣襯著肌膚如雪稼虎。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,046評(píng)論 1 285
  • 那天招刨,我揣著相機(jī)與錄音霎俩,去河邊找鬼。 笑死沉眶,一個(gè)胖子當(dāng)著我的面吹牛打却,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播谎倔,決...
    沈念sama閱讀 38,351評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼柳击,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了片习?” 一聲冷哼從身側(cè)響起捌肴,我...
    開(kāi)封第一講書(shū)人閱讀 36,988評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤蹬叭,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后状知,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體秽五,經(jīng)...
    沈念sama閱讀 43,476評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評(píng)論 2 324
  • 正文 我和宋清朗相戀三年试幽,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了筝蚕。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,064評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡铺坞,死狀恐怖起宽,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情济榨,我是刑警寧澤坯沪,帶...
    沈念sama閱讀 33,712評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站擒滑,受9級(jí)特大地震影響腐晾,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜丐一,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評(píng)論 3 307
  • 文/蒙蒙 一藻糖、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧库车,春花似錦巨柒、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,264評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至珍坊,卻和暖如春牺勾,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背阵漏。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,486評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工驻民, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人履怯。 一個(gè)月前我還...
    沈念sama閱讀 45,511評(píng)論 2 354
  • 正文 我出身青樓回还,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親虑乖。 傳聞我的和親對(duì)象是個(gè)殘疾皇子懦趋,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評(píng)論 2 345

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

  • 本文是我自己在秋招復(fù)習(xí)時(shí)的讀書(shū)筆記,整理的知識(shí)點(diǎn)疹味,也是為了防止忘記仅叫,尊重勞動(dòng)成果帜篇,轉(zhuǎn)載注明出處哦!如果你也喜歡诫咱,那...
    波波波先森閱讀 12,276評(píng)論 6 86
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理笙隙,服務(wù)發(fā)現(xiàn),斷路器坎缭,智...
    卡卡羅2017閱讀 134,599評(píng)論 18 139
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,748評(píng)論 6 342
  • “他們本來(lái)應(yīng)不屬于那里但他們確實(shí)在” ----《亞當(dāng)》 亞當(dāng)和貝絲到最后還是分開(kāi)了竟痰。雖然可惜但這就是現(xiàn)...
    John_Yu閱讀 520評(píng)論 0 0
  • 步入 大宋的地盤(pán) 翻看 水滸傳 108將的名字 是否 還在流傳 打開(kāi)那扇窗 把美女 看見(jiàn) 王婆的紅線 制造著 曠世...
    小白楊老師閱讀 389評(píng)論 0 9