spring源碼閱讀2-2——bean的管理

我們?cè)?a href="http://www.reibang.com/p/8cdc86aeee53" target="_blank">《spring源碼閱讀2-1——bean的管理》中,摸清了BeanFactory家族的整體框架和功能概覽兴猩,本文將繼續(xù)剖析spring如何將BeanFactory一點(diǎn)一點(diǎn)實(shí)現(xiàn)的浅缸。

首先是上次已經(jīng)獲得的BeanFactory家族類(lèi)圖卖漫,本人在這上方加了備注柑船,希望能讓大家看的更為清晰澎怒。


BeanFactory家族類(lèi)圖
SimpleAliasRegistry

這個(gè)類(lèi)實(shí)現(xiàn)了AliasRegistry接口

alias注冊(cè)管理

首先是registerAlias()方法的實(shí)現(xiàn):
為了方便讀者閱讀杭抠,加入了中文注釋

源碼:
    private final Map<String, String> aliasMap = new ConcurrentHashMap<String, String>(16);

    @Override
    public void registerAlias(String name, String alias) {
        Assert.hasText(name, "'name' must not be empty");    //參數(shù)校驗(yàn)
        Assert.hasText(alias, "'alias' must not be empty");
        if (alias.equals(name)) {    //保障了不存在與正名相同的別名
            this.aliasMap.remove(alias);
        }
        else {
            if (!allowAliasOverriding()) {  //是否開(kāi)啟覆蓋注冊(cè)脸甘,默認(rèn)為true
                String registeredName = this.aliasMap.get(alias);
                if (registeredName != null && !registeredName.equals(name)) {
                    throw new IllegalStateException("Cannot register alias '" + alias + "' for name '" +
                            name + "': It is already registered for name '" + registeredName + "'.");
                }
            }
            checkForAliasCircle(name, alias);  //確保添加的沒(méi)有name和alias值相反的數(shù)據(jù)且alias和name不相等
            this.aliasMap.put(alias, name);
        }
    }

---------------------------- 方法封裝 ------------------------------------------------------

    public String canonicalName(String name) {
        String canonicalName = name;
        // Handle aliasing...
        String resolvedName;
        do {
            resolvedName = this.aliasMap.get(canonicalName);
            if (resolvedName != null) {
                canonicalName = resolvedName;
            }
        }
        while (resolvedName != null);
        return canonicalName;
    }

    protected void checkForAliasCircle(String name, String alias) {
        if (alias.equals(canonicalName(name))) {
            throw new IllegalStateException("Cannot register alias '" + alias +
                    "' for name '" + name + "': Circular reference - '" +
                    name + "' is a direct or indirect alias for '" + alias + "' already");
        }
    }

解讀:
第一句是聲明了一個(gè)線程安全的散列表對(duì)象(ConcurrentHashMap)來(lái)作為緩存(如果對(duì)線程安全和非線程安全的集合類(lèi)不太清楚,建議可以系統(tǒng)地去學(xué)習(xí))來(lái)存放注冊(cè)的alias偏灿。
aliasMap以alias為鍵斤程,name為值,其中alias為別名菩混,而name是正名忿墅,如下圖所示。

alias散列表

從代碼中我們看到我們可以為把a(bǔ)lias當(dāng)做name來(lái)再次注冊(cè)別名沮峡,舉個(gè)例子疚脐,叫"張三"的人有兩個(gè)外號(hào):"小張"和"小三",那我可以為小三注冊(cè)別名邢疙,叫"三哥"棍弄,即name="小三", alias="三哥"(當(dāng)然name="張三"也是可以的)。但是不能注冊(cè)name和alias正好相反的數(shù)據(jù)疟游,以及name和alias相等的數(shù)據(jù)呼畸。為什么這么設(shè)計(jì)呢?后面會(huì)給出解釋颁虐。

源碼:
    @Override
    public void removeAlias(String alias) {
        String name = this.aliasMap.remove(alias);    
        if (name == null) {
            throw new IllegalStateException("No alias '" + alias + "' registered");
        }
    }

    @Override
    public boolean isAlias(String name) {
        return this.aliasMap.containsKey(name);
    }

    @Override
    public String[] getAliases(String name) {
        List<String> result = new ArrayList<String>();
        synchronized (this.aliasMap) {
            retrieveAliases(name, result);
        }
        return StringUtils.toStringArray(result);
    }

    //遍歷散列表蛮原,獲取name為定值的所有alias
    private void retrieveAliases(String name, List<String> result) {
        for (Map.Entry<String, String> entry : this.aliasMap.entrySet()) {
            String registeredName = entry.getValue();
            if (registeredName.equals(name)) {
                String alias = entry.getKey();
                result.add(alias);
                retrieveAliases(alias, result);  //注意是有回調(diào)的哦
            }
        }
    }

解讀:
removeAlias()isAlias()就不說(shuō)了,根據(jù)name來(lái)獲取alias的方法getAliases(String name)另绩,由于散列表是以alias為鍵儒陨,而name為值,要想找出滿足條件的所有alias值笋籽,就必須對(duì)散列表進(jìn)行遍歷蹦漠,找出所有name和給定值相等的值。
我在注釋中注明了是有回調(diào)函數(shù)存在的车海,當(dāng)找到name值和給定值相等是笛园,會(huì)把這個(gè)alias作為name,再次對(duì)散列表進(jìn)行遍歷,如下圖所示

回調(diào)的效果

返回值中有"三哥"研铆,而三哥對(duì)應(yīng)的name是"小三"埋同。這也是在注冊(cè)時(shí)限制注冊(cè)的數(shù)據(jù)name和alias不能正好相反且name和alias不能相等的原因,否則回調(diào)函數(shù)將陷入無(wú)限循環(huán)中去蚜印。
因此莺禁,在設(shè)計(jì)階段和編程過(guò)程中,如果涉及回調(diào)窄赋,就要規(guī)避無(wú)限循環(huán)的可能哟冬。
至此,我們已經(jīng)閱讀完了這個(gè)類(lèi)的所有方法(還有一個(gè)resolveAliases()入?yún)⑹且粋€(gè)較為復(fù)雜的對(duì)象忆绰,暫時(shí)不解釋?zhuān)认禄嘏龅搅嗽偌?xì)究)

DefautlSingletonBeanRegistry

該類(lèi)繼承了SimpleAliasRegistry浩峡,并且實(shí)現(xiàn)SingletonBeanRegistry接口。
同樣的错敢,來(lái)看下是如何實(shí)現(xiàn)registerSingleton(String beanName, Object singletonObject)方法:

源碼:
    protected static final Object NULL_OBJECT = new Object();

    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(64);

    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);

    private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);

    private final Set<String> registeredSingletons = new LinkedHashSet<String>(64);

    @Override
    public void registerSingleton(String beanName, Object singletonObject) throws IllegalStateException {
        Assert.notNull(beanName, "'beanName' must not be null");
        synchronized (this.singletonObjects) {
            Object oldObject = this.singletonObjects.get(beanName);
            if (oldObject != null) {
                throw new IllegalStateException("Could not register object [" + singletonObject +
                        "] under bean name '" + beanName + "': there is already object [" + oldObject + "] bound");
            }
            addSingleton(beanName, singletonObject);
        }
    }

    protected void addSingleton(String beanName, Object singletonObject) {
        synchronized (this.singletonObjects) {
            this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));
            this.singletonFactories.remove(beanName);
            this.earlySingletonObjects.remove(beanName);
            this.registeredSingletons.add(beanName);
        }
    }

解讀:
同SimpleAliasRegistry翰灾,這里也是用散列表來(lái)做緩存。但是SingleBean的注冊(cè)要來(lái)的復(fù)雜稚茅,因?yàn)閎ean還涉及到初始化的問(wèn)題纸淮,因此這里有多個(gè)緩存用的對(duì)象:

  • singletonObjects是一個(gè)線程安全的散列表,用于存放已注冊(cè)的SingleBean實(shí)例
  • singletonFactories用來(lái)存放初始化SingleBean實(shí)例的factory對(duì)象
  • earlySingletonObjects 用來(lái)存放已存在但是未注冊(cè)的SingleBean實(shí)例
  • registeredSingletons是一個(gè)集合亚享,存放的是已注冊(cè)的SingleBean對(duì)象的名稱

從代碼中可以看到咽块,當(dāng)一個(gè)Bean被注冊(cè)后,就會(huì)將這個(gè)Bean實(shí)例添加到緩存中去欺税,如果這個(gè)實(shí)例存在對(duì)應(yīng)的BeanFactory侈沪,則BeanFactory將被移除,或者這個(gè)Bean存在于earlySingletonObjects中晚凿,也將從earlySingletonObjects中移除亭罪。
這個(gè)過(guò)程可能不太好理解,舉個(gè)例子來(lái)說(shuō)明吧:
有a歼秽,b应役,c三個(gè)學(xué)生要在大學(xué)注冊(cè),a社會(huì)自考招生哲银,沒(méi)有學(xué)籍扛吞;b是高中FactoryB的學(xué)生;c是該大學(xué)的留級(jí)生荆责,學(xué)籍已存在。b注冊(cè)完成后亚脆,要將學(xué)籍檔案轉(zhuǎn)移到大學(xué)中做院,c也要將學(xué)籍檔案轉(zhuǎn)移到當(dāng)前年級(jí)的檔案庫(kù)中去(如下圖所示)

Bean注冊(cè)圖解
    private final Set<String> singletonsCurrentlyInCreation =
            Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>(16));
    @Override
    public Object getSingleton(String beanName) {
        return getSingleton(beanName, true);
    }

    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            synchronized (this.singletonObjects) {
                singletonObject = this.earlySingletonObjects.get(beanName);
                if (singletonObject == null && allowEarlyReference) {
                    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
                        singletonObject = singletonFactory.getObject();
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }
        return (singletonObject != NULL_OBJECT ? singletonObject : null);
    }

    public boolean isSingletonCurrentlyInCreation(String beanName) {
        return this.singletonsCurrentlyInCreation.contains(beanName);
    }

解讀:
這段邏輯比較清晰了,還是那上面那個(gè)例子來(lái)講,要獲得一個(gè)學(xué)生的學(xué)籍檔案键耕,首先直接從該年級(jí)的學(xué)籍檔案中查看寺滚,如果沒(méi)有,這個(gè)學(xué)生是否為該入學(xué)注冊(cè)的學(xué)生(isSingletonCurrentlyIncreation()方法判斷)屈雄,如果是村视,就依次查找earlySingletonObjects和singletonFactories對(duì)象。
再看源碼的過(guò)程中酒奶,會(huì)有很多疑惑蚁孔,比如這里為什么要設(shè)置這么多的緩存對(duì)象,想alias那樣注冊(cè)不就好了惋嚎,為什么要那么麻煩呢杠氢?但是,要沉住氣另伍,堅(jiān)持下去鼻百,最終會(huì)讓你豁然開(kāi)朗。這也許是源碼閱讀的魅力所在吧

源碼:
    @Override
    public boolean containsSingleton(String beanName) {
        return this.singletonObjects.containsKey(beanName);
    }

    @Override
    public String[] getSingletonNames() {
        synchronized (this.singletonObjects) {
            return StringUtils.toStringArray(this.registeredSingletons);
        }
    }

    @Override
    public int getSingletonCount() {
        synchronized (this.singletonObjects) {
            return this.registeredSingletons.size();
        }
    }

這幾個(gè)方法的重寫(xiě)就不說(shuō)了摆尝,和alias的注冊(cè)管理是一樣的温艇。

源碼:
    private final Map<String, Set<String>> dependentBeanMap = new ConcurrentHashMap<String, Set<String>>(64);

    private final Map<String, Set<String>> dependenciesForBeanMap = new ConcurrentHashMap<String, Set<String>>(64);

    public void registerDependentBean(String beanName, String dependentBeanName) {
        // A quick check for an existing entry upfront, avoiding synchronization...
        String canonicalName = canonicalName(beanName);
        Set<String> dependentBeans = this.dependentBeanMap.get(canonicalName);
        if (dependentBeans != null && dependentBeans.contains(dependentBeanName)) {
            return;
        }

        // No entry yet -> fully synchronized manipulation of the dependentBeans Set
        synchronized (this.dependentBeanMap) {
            dependentBeans = this.dependentBeanMap.get(canonicalName);
            if (dependentBeans == null) {
                dependentBeans = new LinkedHashSet<String>(8);
                this.dependentBeanMap.put(canonicalName, dependentBeans);
            }
            dependentBeans.add(dependentBeanName);
        }
        synchronized (this.dependenciesForBeanMap) {
            Set<String> dependenciesForBean = this.dependenciesForBeanMap.get(dependentBeanName);
            if (dependenciesForBean == null) {
                dependenciesForBean = new LinkedHashSet<String>(8);
                this.dependenciesForBeanMap.put(dependentBeanName, dependenciesForBean);
            }
            dependenciesForBean.add(canonicalName);
        }
    }

    protected boolean isDependent(String beanName, String dependentBeanName) {
        String canonicalName = canonicalName(beanName);
        Set<String> dependentBeans = this.dependentBeanMap.get(canonicalName);
        if (dependentBeans == null) {
            return false;
        }
        if (dependentBeans.contains(dependentBeanName)) {
            return true;
        }
        for (String transitiveDependency : dependentBeans) {
            if (isDependent(transitiveDependency, dependentBeanName)) {
                return true;
            }
        }
        return false;
    }

    protected boolean hasDependentBean(String beanName) {
        return this.dependentBeanMap.containsKey(beanName);
    }

    public String[] getDependentBeans(String beanName) {
        Set<String> dependentBeans = this.dependentBeanMap.get(beanName);
        if (dependentBeans == null) {
            return new String[0];
        }
        return StringUtils.toStringArray(dependentBeans);
    }

解讀:
DefaultSingletonBeanRegistry不僅實(shí)現(xiàn)了SingleBeanRegistry中的對(duì)SingleBean注冊(cè)管理的方法,還擴(kuò)展了對(duì)于SingleBean的依賴關(guān)系進(jìn)行管理堕汞。
緩存對(duì)象:

  • dependentBeanMap 存儲(chǔ)bean所依賴的其他bean組件
  • dependenciesForBeanMap 存儲(chǔ)當(dāng)前bean被哪些其他bean組件依賴

方法:

  • registerDependentBean() ==> 注冊(cè)bean所依賴的dependentBean
  • isDependent() ==> 返回denpendentBean是否為bean的依賴bean
  • hasDenpendentBean() ==> 返回bean是否有dependentBean
  • getDependentBeans() ==> 返回bean的所有dependentBean
  • destroyBean() ==> 刪除一個(gè)bean勺爱,刪除其依賴信息
總結(jié)
總結(jié).png
本文到這就要結(jié)束了,回顧下
    本文主要對(duì)Factory家族中的SimpleAliasRegistry和DefaultSingletonBeanRegistry進(jìn)行分析臼朗。
其主要通過(guò)線程安全的散列表做緩存邻寿,實(shí)現(xiàn)了alias管理、SingleBean管理以及Bean依賴的管理的功能视哑。

本文到此就結(jié)束绣否,希望能夠幫到大家。
純屬原創(chuàng)挡毅,轉(zhuǎn)載請(qǐng)聲明出處蒜撮。
                                                    ——作者:陳來(lái)件(QQ:810522442)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市跪呈,隨后出現(xiàn)的幾起案子段磨,更是在濱河造成了極大的恐慌,老刑警劉巖耗绿,帶你破解...
    沈念sama閱讀 217,734評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件苹支,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡误阻,警方通過(guò)查閱死者的電腦和手機(jī)债蜜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén)晴埂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人寻定,你說(shuō)我怎么就攤上這事儒洛。” “怎么了狼速?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,133評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵琅锻,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我向胡,道長(zhǎng)恼蓬,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,532評(píng)論 1 293
  • 正文 為了忘掉前任捷枯,我火速辦了婚禮滚秩,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘淮捆。我一直安慰自己郁油,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,585評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布攀痊。 她就那樣靜靜地躺著,像睡著了一般苟径。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蟆盐,一...
    開(kāi)封第一講書(shū)人閱讀 51,462評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音遭殉,去河邊找鬼石挂。 笑死险污,一個(gè)胖子當(dāng)著我的面吹牛痹愚,可吹牛的內(nèi)容都是我干的蛔糯。 我是一名探鬼主播拯腮,決...
    沈念sama閱讀 40,262評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼蚁飒!你這毒婦竟也來(lái)了袖瞻?” 一聲冷哼從身側(cè)響起易核,我...
    開(kāi)封第一講書(shū)人閱讀 39,153評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤蜒灰,失蹤者是張志新(化名)和其女友劉穎肩碟,沒(méi)想到半個(gè)月后凸椿,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體削祈,經(jīng)...
    沈念sama閱讀 45,587評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡脑漫,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,792評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了吨拍。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片网杆。...
    茶點(diǎn)故事閱讀 39,919評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖碳却,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情昼浦,我是刑警寧澤,帶...
    沈念sama閱讀 35,635評(píng)論 5 345
  • 正文 年R本政府宣布鸟蟹,位于F島的核電站使兔,受9級(jí)特大地震影響建钥,放射性物質(zhì)發(fā)生泄漏火诸。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,237評(píng)論 3 329
  • 文/蒙蒙 一奈搜、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧馋吗,春花似錦秋秤、人聲如沸宏粤。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,855評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至沃于,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間繁莹,已是汗流浹背特幔。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,983評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蚯斯,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,048評(píng)論 3 370
  • 正文 我出身青樓溉跃,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親嵌牺。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,864評(píng)論 2 354

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