Spring(六)核心容器 - 注冊(cè)單例 Bean 實(shí)例避消、SingletonBeanRegistry 簡(jiǎn)介

前言

上篇文章我們對(duì)注冊(cè) Bean 的核心類 BeanDefinitionRegistry 進(jìn)行了討論,這里的注冊(cè) Bean 是指保存 Bean 的相關(guān)信息,也就是將 Bean 定義成 BeanDefinition杂彭,然后放入容器中。除此之外茬故,Spring 還提供一個(gè)統(tǒng)一操作單例 Bean 實(shí)例的類 SingletonBeanRegistry盖灸,通過該類可直接對(duì)單例 Bean 的實(shí)例進(jìn)行存儲(chǔ)、注冊(cè)等操作磺芭。

SingletonBeanRegistry

SingletonBeanRegistry 是一個(gè)接口赁炎,其定義了操作單例 Bean 實(shí)例的一些基礎(chǔ)方法:

public interface SingletonBeanRegistry {

    // 注冊(cè)單例 Bean。其實(shí)就是將該 Bean 保存到一個(gè)專門存儲(chǔ)單例 Bean 實(shí)例的Map中,Key是 beanName徙垫,Value是對(duì)應(yīng)的單例 Bean 實(shí)例
    void registerSingleton(String beanName, Object singletonObject);

    // 通過 beanName 獲取該單例 Bean 實(shí)例
    Object getSingleton(String beanName);

    // 通過 beanName 判斷該單例 Bean 實(shí)例是否存在
    boolean containsSingleton(String beanName);

    // 返回所有單例 Bean 的名稱
    String[] getSingletonNames();

    // 返回已注冊(cè)的單例 Bean 實(shí)例數(shù)量
    int getSingletonCount();

    // 返回當(dāng)前使用的單例鎖讥裤,主要提供給外部協(xié)作者使用
    Object getSingletonMutex();
}

這個(gè)接口的核心實(shí)現(xiàn)類是 DefaultSingletonBeanRegistry,該類不僅實(shí)現(xiàn)了這些基礎(chǔ)方法姻报,還針對(duì)單例 Bean 擴(kuò)展了許多功能己英,如:存儲(chǔ) Bean 之間的依賴關(guān)系胯杭、存儲(chǔ) Bean 的包含關(guān)系(外部類包含內(nèi)部類)上遥、獲取 Bean 所處的狀態(tài)(正在創(chuàng)建峦萎、創(chuàng)建完畢等)藕漱、回調(diào)銷毀 Bean 時(shí)觸發(fā)的 destroy 方法等糯景。

下面是 DefaultSingletonBeanRegistry 類中的核心屬性和方法:

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {

    /********** 1世剖、定義的一些 Map 屬性逃糟,用來保存單例 Bean 實(shí)例捷绑、 Bean 的依賴關(guān)系 **********/

    // 緩存單例 Bean 實(shí)例笆焰,Key 是 beanName劫谅,Value 是單例 Bean 實(shí)例
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

    // 緩存 Bean 對(duì)應(yīng)的 ObjectFactory 
    //      ObjectFactory 是獲取 Bean 實(shí)例的工廠,只不過這里獲取的 Bean 還未完全實(shí)例化嚷掠,屬于提早暴露的 Bean
    //      該屬性在解決循環(huán)依賴時(shí)使用捏检,后續(xù)會(huì)深入討論
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

    // 緩存 singletonFactories 屬性中通過 ObjectFactory 創(chuàng)建的 Bean
    //      該屬性也是在解決循環(huán)依賴時(shí)使用,后續(xù)會(huì)深入討論
    private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

    // 保存已注冊(cè)的單例 Bean 名稱
    private final Set<String> registeredSingletons = new LinkedHashSet<>(256);

    // 保存當(dāng)前正在創(chuàng)建的 Bean 的名稱
    private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));

    // 保存當(dāng)前從創(chuàng)建檢查中排除的 Bean 的名稱
    private final Set<String> inCreationCheckExclusions = Collections.newSetFromMap(new ConcurrentHashMap<>(16));

    ...
    
    // 當(dāng)前 Bean 是否處于銷毀狀態(tài)
    private boolean singletonsCurrentlyInDestruction = false;

    // 保存實(shí)現(xiàn)了 DisposableBean 接口的 Bean不皆,在銷毀 Bean 時(shí)贯城,會(huì)回調(diào)該 Bean 中的 destory 方法
    private final Map<String, Object> disposableBeans = new LinkedHashMap<>();

    // 保存 Bean 的包含關(guān)系,key 是 Bean 的名稱霹娄,value 是 Bean 里面包含的其它 Bean 名稱集合
    private final Map<String, Set<String>> containedBeanMap = new ConcurrentHashMap<>(16);

    // 保存 Bean 的依賴關(guān)系:key 是 Bean 的名稱冤狡,value 是依賴于該 Bean 的其它 Bean 名稱集合
    private final Map<String, Set<String>> dependentBeanMap = new ConcurrentHashMap<>(64);

    // 保存 Bean 的依賴關(guān)系:key 是 Bean 的名稱,value 是該 Bean 所依賴的其它 Bean 名稱集合
    private final Map<String, Set<String>> dependenciesForBeanMap = new ConcurrentHashMap<>(64);

    /******************************** 2项棠、注冊(cè)單例 Bean 實(shí)例及對(duì)應(yīng)的實(shí)例工廠 ********************************/

    // 注冊(cè)單例 Bean 實(shí)例
    @Override
    public void registerSingleton(String beanName, Object singletonObject) throws IllegalStateException {
        Assert.notNull(beanName, "Bean name must not be null");
        Assert.notNull(singletonObject, "Singleton object must not be null");
        
        synchronized (this.singletonObjects) {
            // 通過 beanName 獲取 Map 中對(duì)應(yīng)的單例 Bean 實(shí)例
            Object oldObject = this.singletonObjects.get(beanName);
            
            //  如果不為空悲雳,則拋出異常,因?yàn)閱卫呀?jīng)存在香追,無法再次注冊(cè)
            if (oldObject != null) {
                throw new IllegalStateException("Could not register object [" + singletonObject +
                        "] under bean name '" + beanName + "': there is already object [" + oldObject + "] bound");
            }
            
            // 為空合瓢,則進(jìn)入 addSingleton 方法
            addSingleton(beanName, singletonObject);
        }
    }

    // 緩存單例 Bean 實(shí)例
    protected void addSingleton(String beanName, Object singletonObject) {
        synchronized (this.singletonObjects) {
            // 將單例 Bean 實(shí)例存放至 singletonObjects 集合
            this.singletonObjects.put(beanName, singletonObject);
            
            // 當(dāng) beanName 對(duì)應(yīng)的 Bean 實(shí)例已被存放至 singletonObjects 集合時(shí),singletonFactories 
            // 和 earlySingletonObjects 集合則不能再持有 beanName 對(duì)應(yīng)的 ObjectFactory 和實(shí)例
            // 其中原因會(huì)在后續(xù)循環(huán)依賴的文章深入討論
            this.singletonFactories.remove(beanName);
            this.earlySingletonObjects.remove(beanName);
            
            // 存儲(chǔ) Bean 名稱
            this.registeredSingletons.add(beanName);
        }
    }

    // 緩存 Bean 對(duì)應(yīng)的 ObjectFactory
    protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
        Assert.notNull(singletonFactory, "Singleton factory must not be null");
        synchronized (this.singletonObjects) {
            if (!this.singletonObjects.containsKey(beanName)) {
                this.singletonFactories.put(beanName, singletonFactory);
                this.earlySingletonObjects.remove(beanName);
                this.registeredSingletons.add(beanName);
            }
        }
    }

    /********************************* 3透典、獲取單例 Bean 實(shí)例 *********************************/
    
    @Override
    public Object getSingleton(String beanName) {
        
        // 該方法較為復(fù)雜晴楔,在后續(xù)結(jié)合循環(huán)依賴的場(chǎng)景討論
        
    }

    ...

    /***************************** 4、對(duì)單例 Bean 實(shí)例的基礎(chǔ)操作 *****************************/

    // 刪除單例 Bean 實(shí)例
    protected void removeSingleton(String beanName) {
        synchronized (this.singletonObjects) {
            this.singletonObjects.remove(beanName);
            this.singletonFactories.remove(beanName);
            this.earlySingletonObjects.remove(beanName);
            this.registeredSingletons.remove(beanName);
        }
    }

    // 判斷 beanName 對(duì)應(yīng)的單例 Bean 實(shí)例時(shí)候存在
    @Override
    public boolean containsSingleton(String beanName) {
        return this.singletonObjects.containsKey(beanName);
    }

    // 返回所有單例 Bean 的 beanName
    @Override
    public String[] getSingletonNames() {
        synchronized (this.singletonObjects) {
            return StringUtils.toStringArray(this.registeredSingletons);
        }
    }

    // 返回單例 Bean 實(shí)例數(shù)量
    @Override
    public int getSingletonCount() {
        synchronized (this.singletonObjects) {
            return this.registeredSingletons.size();
        }
    }

    ...

    /*************************************** 5峭咒、 Bean 的狀態(tài) **************************************/

    // beanName 對(duì)應(yīng)的 Bean 是否處于實(shí)例化階段
    public boolean isCurrentlyInCreation(String beanName) {
        Assert.notNull(beanName, "Bean name must not be null");
        return (!this.inCreationCheckExclusions.contains(beanName) && isActuallyInCreation(beanName));
    }
    protected boolean isActuallyInCreation(String beanName) {
        return isSingletonCurrentlyInCreation(beanName);
    }
    public boolean isSingletonCurrentlyInCreation(String beanName) {
        return this.singletonsCurrentlyInCreation.contains(beanName);
    }

    // 單例 Bean 實(shí)例化前執(zhí)行税弃,將正要?jiǎng)?chuàng)建的 Bean 加入 singletonsCurrentlyInCreation 集合
    protected void beforeSingletonCreation(String beanName) {
        if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }
    }

    // 單例 Bean 實(shí)例化后執(zhí)行,從 singletonsCurrentlyInCreation 集合中移除已創(chuàng)建的 Bean 
    protected void afterSingletonCreation(String beanName) {
        if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
            throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
        }
    }

    ...

    /********************* 6凑队、 存儲(chǔ) Bean 之間的關(guān)系则果、判斷 Bean 之間的關(guān)系 *********************/
    
    // 保存具有包含關(guān)系的 Bean(內(nèi)部類與外部類)
    public void registerContainedBean(String containedBeanName, String containingBeanName) {
        synchronized (this.containedBeanMap) {
            Set<String> containedBeans =
                    this.containedBeanMap.computeIfAbsent(containingBeanName, k -> new LinkedHashSet<>(8));
            if (!containedBeans.add(containedBeanName)) {
                return;
            }
        }
        registerDependentBean(containedBeanName, containingBeanName);
    }

    // 保存具有依賴關(guān)系的 Bean
    public void registerDependentBean(String beanName, String dependentBeanName) {
        String canonicalName = canonicalName(beanName);

        synchronized (this.dependentBeanMap) {
            Set<String> dependentBeans =
                    this.dependentBeanMap.computeIfAbsent(canonicalName, k -> new LinkedHashSet<>(8));
            if (!dependentBeans.add(dependentBeanName)) {
                return;
            }
        }

        synchronized (this.dependenciesForBeanMap) {
            Set<String> dependenciesForBean =
                    this.dependenciesForBeanMap.computeIfAbsent(dependentBeanName, k -> new LinkedHashSet<>(8));
            dependenciesForBean.add(canonicalName);
        }
    }

    ...
    
    /***************************** 7、 銷毀 Bean 的方法 *****************************/
    
    ...
}

DefaultSingletonBeanRegistry 類中的屬性及方法雖然很多,但也有規(guī)律可循的西壮,大致分為對(duì)單例 Bean 實(shí)例的操作遗增、管理 Bean 之間關(guān)系、針對(duì) Bean 的不同狀態(tài)進(jìn)行操作及銷毀 Bean 的操作款青。

該類中的核心還是那些 Map做修,類中的所有方法都是對(duì)這些 Map 進(jìn)行操作,而這些 Map 中存儲(chǔ)的是不同場(chǎng)景下的單例 Bean 抡草。

最后

關(guān)于 SingletonBeanRegistry 就介紹到這饰及,其主要還是針對(duì)單例 Bean 進(jìn)行操作,外部調(diào)用者統(tǒng)一繼承該類操作單例 Bean康震,其主要調(diào)用者還是 DefaultListableBeanFactory旋炒,前篇文章也說過,這是我們當(dāng)前上下文環(huán)境中使用的 BeanFactory 工廠類签杈,在工廠類中執(zhí)行 getBean 操作時(shí),會(huì)調(diào)用這些方法鼎兽,后續(xù)會(huì)詳細(xì)討論 getBean 操作答姥。最后值得注意是,Spring 也是在該類中解決循環(huán)依賴問題谚咬,這部分也會(huì)在后面詳細(xì)討論鹦付。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市择卦,隨后出現(xiàn)的幾起案子敲长,更是在濱河造成了極大的恐慌,老刑警劉巖秉继,帶你破解...
    沈念sama閱讀 212,029評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件祈噪,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡尚辑,警方通過查閱死者的電腦和手機(jī)辑鲤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,395評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來杠茬,“玉大人月褥,你說我怎么就攤上這事∑昂恚” “怎么了宁赤?”我有些...
    開封第一講書人閱讀 157,570評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)栓票。 經(jīng)常有香客問我决左,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,535評(píng)論 1 284
  • 正文 為了忘掉前任哆窿,我火速辦了婚禮链烈,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘挚躯。我一直安慰自己强衡,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,650評(píng)論 6 386
  • 文/花漫 我一把揭開白布码荔。 她就那樣靜靜地躺著漩勤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪缩搅。 梳的紋絲不亂的頭發(fā)上越败,一...
    開封第一講書人閱讀 49,850評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音硼瓣,去河邊找鬼究飞。 笑死,一個(gè)胖子當(dāng)著我的面吹牛堂鲤,可吹牛的內(nèi)容都是我干的亿傅。 我是一名探鬼主播,決...
    沈念sama閱讀 39,006評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼瘟栖,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼葵擎!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起半哟,我...
    開封第一講書人閱讀 37,747評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤酬滤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后寓涨,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體盯串,經(jīng)...
    沈念sama閱讀 44,207評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,536評(píng)論 2 327
  • 正文 我和宋清朗相戀三年戒良,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了嘴脾。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,683評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蔬墩,死狀恐怖译打,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情拇颅,我是刑警寧澤奏司,帶...
    沈念sama閱讀 34,342評(píng)論 4 330
  • 正文 年R本政府宣布,位于F島的核電站樟插,受9級(jí)特大地震影響韵洋,放射性物質(zhì)發(fā)生泄漏竿刁。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,964評(píng)論 3 315
  • 文/蒙蒙 一搪缨、第九天 我趴在偏房一處隱蔽的房頂上張望食拜。 院中可真熱鬧,春花似錦副编、人聲如沸负甸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,772評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽呻待。三九已至,卻和暖如春队腐,著一層夾襖步出監(jiān)牢的瞬間蚕捉,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,004評(píng)論 1 266
  • 我被黑心中介騙來泰國打工柴淘, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留迫淹,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,401評(píng)論 2 360
  • 正文 我出身青樓为严,卻偏偏與公主長(zhǎng)得像敛熬,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子梗脾,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,566評(píng)論 2 349

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