Spring(五)核心容器 - 注冊(cè) Bean、BeanDefinitionRegistry 簡(jiǎn)介

前言

上篇文章我們對(duì) BeanDefinition 進(jìn)行了討論,BeanDefinition 是對(duì) Bean 的定義,其保存了 Bean 的各種信息,如屬性、構(gòu)造方法參數(shù)、是否單例、是否延遲加載等。這里的注冊(cè) Bean 是指將 Bean 定義成 BeanDefinition,之后放入 Spring 容器中,我們常說的容器其實(shí)就是 Beanfactory 中的一個(gè) Map,key 是 Bean 的名稱,value 是 Bean 對(duì)應(yīng)的 BeanDefinition,這個(gè)注冊(cè) Bean 的方法由 BeanFactory 子類實(shí)現(xiàn)。

注:本篇文章使用的 SpringBoot 版本為 2.0.3.RELEASE,其 Spring 版本為 5.0.7.RELEASE

正文

在前面的《Spring(三)核心容器 - ApplicationContext 上下文啟動(dòng)準(zhǔn)備》文章中說過,當(dāng)前環(huán)境的 BeanFactory 實(shí)現(xiàn)類是 DefaultListableBeanFactory,是一個(gè)具有注冊(cè)功能的完整 Bean 工廠,注冊(cè) Bean 的方法是 registerBeanDefinition,DefaultListableBeanFactory 通過實(shí)現(xiàn) BeanDefinitionRegistry 接口,重寫該方法。

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
        implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {

    ...

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

討論 registerBeanDefinition 方法之前,先來簡(jiǎn)單介紹 BeanDefinitionRegistry 接口。

1、BeanDefinitionRegistry 簡(jiǎn)介

BeanDefinitionRegistry 是一個(gè)接口,它定義了關(guān)于 BeanDefinition 的注冊(cè)、移除、查詢等一系列的操作。

public interface BeanDefinitionRegistry extends AliasRegistry {

    // 注冊(cè) BeanDefinition
    void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException;

    // 移除 BeanDefinition
    void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;

    // 獲取 BeanDefinition
    BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;

    // 根據(jù) beanName 判斷容器是否存在對(duì)應(yīng)的 BeanDefinition 
    boolean containsBeanDefinition(String beanName);

    // 獲取所有的 BeanDefinition
    String[] getBeanDefinitionNames();

    // 獲取 BeanDefinition 數(shù)量
    int getBeanDefinitionCount();

    // 判斷 beanName 是否被占用
    boolean isBeanNameInUse(String beanName);
}

該接口有三個(gè)實(shí)現(xiàn)類:DefaultListableBeanFactory、GenericApplicationContext、SimpleBeanDefinitionRegistry,其中 GenericApplicationContext 底層調(diào)用的是 DefaultListableBeanFactory 中的實(shí)現(xiàn)方法,所以嚴(yán)格意義上來說,只有兩個(gè)實(shí)現(xiàn)類。這里,我們主要討論 DefaultListableBeanFactory 中的方法實(shí)現(xiàn)。

2、registerBeanDefinition 方法注冊(cè) Bean

前面說過 registerBeanDefinition 方法的主要實(shí)現(xiàn)類是 DefaultListableBeanFactory :

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
    
    ...
    
    // 存儲(chǔ)所有的 BeanDefinition ,key 是 Bean 的名稱。我們一直稱呼的容器,底層就是這個(gè) Map
    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
    // 存儲(chǔ)所有 Bean 名稱
    private volatile List<String> beanDefinitionNames = new ArrayList<>(256);
    // 存儲(chǔ)手動(dòng)注冊(cè)的單例 Bean 名稱
    private volatile Set<String> manualSingletonNames = new LinkedHashSet<>(16);
    // 存儲(chǔ)凍結(jié)的 BeanDefinition,留作后面緩存用
    private volatile String[] frozenBeanDefinitionNames;
    
    ...
    
    // 方法的入?yún)?Bean 名稱和對(duì)應(yīng)的 BeanDefinition
    @Override
    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");

        // 如果 beanDefinition 的實(shí)例為 AbstractBeanDefinition,則進(jìn)行驗(yàn)證
        if (beanDefinition instanceof AbstractBeanDefinition) {
            try {
                // 驗(yàn)證:
                //     如果有重寫方法,但是是工廠方法,則拋出異常,因?yàn)橹貙懛椒ㄐ枰?,而工廠方法無法代理;
                //     通過方法名稱,判斷 Bean 中該名稱方法存在的數(shù)量,0:方法不存在,報(bào)錯(cuò);1:方法非重載,overloaded 屬性設(shè)為 false;
                ((AbstractBeanDefinition) beanDefinition).validate();
            }
            catch (BeanDefinitionValidationException ex) {
                throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                        "Validation of bean definition failed", ex);
            }
        }

        BeanDefinition oldBeanDefinition;
        // 先從 beanDefinitionMap 中嘗試獲取 beanName 對(duì)應(yīng) BeanDefinition
        oldBeanDefinition = this.beanDefinitionMap.get(beanName);
        
        // 不為 null,則 beanName 對(duì)應(yīng)的 BeanDefinition 已經(jīng)存在
        if (oldBeanDefinition != null) {
            
            // 是否應(yīng)允許覆蓋 BeanDefinition,不允許則拋異常
            if (!isAllowBeanDefinitionOverriding()) {
                throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                        "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
                        "': There is already [" + oldBeanDefinition + "] bound.");
            }
            
            /***************************** 若允許覆蓋 *****************************/
            
            // 判斷 Bean 的角色大小:
            //      0:用戶定義的 Bean、1:來源于配置文件的 Bean、2:Spring 內(nèi)部的 Bean;
            // 當(dāng)原 BeanDefinition 角色小于新的 BeanDefinition 角色時(shí),輸出一個(gè) warn 日志,提示 BeanDefinition 被覆蓋
            else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
                // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
                if (this.logger.isWarnEnabled()) {
                    this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +
                            "' with a framework-generated bean definition: replacing [" +
                            oldBeanDefinition + "] with [" + beanDefinition + "]");
                }
            }
            // 當(dāng)新 BeanDefinition 屬性值不等于原 BeanDefinition 屬性值時(shí),輸出 info 提示信息
            else if (!beanDefinition.equals(oldBeanDefinition)) {
                if (this.logger.isInfoEnabled()) {
                    this.logger.info("Overriding bean definition for bean '" + beanName +
                            "' with a different definition: replacing [" + oldBeanDefinition +
                            "] with [" + beanDefinition + "]");
                }
            }
            // 最后,輸出 debug 日志信息:用等效的新 BeanDefinition 覆蓋原 BeanDefinition
            else {
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Overriding bean definition for bean '" + beanName +
                            "' with an equivalent definition: replacing [" + oldBeanDefinition +
                            "] with [" + beanDefinition + "]");
                }
            }
            // 添加至 BeanDefinition 集合,并覆蓋原 BeanDefinition
            this.beanDefinitionMap.put(beanName, beanDefinition);
        }
        
        // Map 中無對(duì)應(yīng)的 BeanDefinition,則直接注冊(cè)
        else {
            // 已開始創(chuàng)建 Bean 
            if (hasBeanCreationStarted()) {
                synchronized (this.beanDefinitionMap) {
                    // 將 Bean 對(duì)應(yīng)的 BeanDefinition 放入 beanDefinitionMap 中
                    this.beanDefinitionMap.put(beanName, beanDefinition);
                    
                    // 創(chuàng)建新的 beanNames 集合,并將已緩存的 beanName 和新的 beanName 加入該集合
                    List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
                    updatedDefinitions.addAll(this.beanDefinitionNames);
                    updatedDefinitions.add(beanName);
                    this.beanDefinitionNames = updatedDefinitions;
                    
                    // 在手動(dòng)注冊(cè) Bean 的集合中,如果存在同名的 beanName,則將集合中同名的 beanName 刪除
                    if (this.manualSingletonNames.contains(beanName)) {
                        Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
                        updatedSingletons.remove(beanName);
                        this.manualSingletonNames = updatedSingletons;
                    }
                }
            }
            // 仍處于啟動(dòng)注冊(cè)階段
            else {
                // 將當(dāng)前 Bean 對(duì)應(yīng)的 BeanDefinition 放入 beanDefinitionMap 中
                this.beanDefinitionMap.put(beanName, beanDefinition);
                // 將當(dāng)前 beanName 放入 beanDefinitionNames
                this.beanDefinitionNames.add(beanName);
                // 刪除手動(dòng)注冊(cè) Bean 集合中同名的 beanName
                this.manualSingletonNames.remove(beanName);
            }
            
            // 將存儲(chǔ)凍結(jié) BeanDefinition 的 Map 置為 null
            this.frozenBeanDefinitionNames = null;
        }

        // 當(dāng)前注冊(cè)的 BeanDefinition 已在 beanDefinitionMap 中存在,或者其實(shí)例已在存儲(chǔ)單例 Bean 的 Map 中存在
        if (oldBeanDefinition != null || containsSingleton(beanName)) {
            
            // 重置 BeanDefinition,主要做一些清理工作
            resetBeanDefinition(beanName);
        }
    }
}

執(zhí)行完 registerBeanDefinition 方法后,Bean 的名稱和對(duì)應(yīng)的 BeanDefinition 就被放入了容器中,后續(xù)獲取 Bean 也是從這個(gè)容器中獲取。

當(dāng)然,DefaultListableBeanFactory 還實(shí)現(xiàn)了 BeanDefinitionRegistry 接口的其它方法,如對(duì) BeanDefinition 進(jìn)行移除、判斷是否存在、獲取數(shù)量等操作,其實(shí)都是圍繞 beanDefinitionMap 這個(gè) Map 進(jìn)行的,這里就不詳細(xì)介紹。

需要注意的是 registerBeanDefinition 方法會(huì)在后面頻繁被調(diào)用,后續(xù)會(huì)逐一提到。

最后

這篇文章主要對(duì)注冊(cè) Bean 的核心方法進(jìn)行討論,但其中涉及到了單例 Bean 實(shí)例注冊(cè),這和當(dāng)前文章的注冊(cè) Bean 不同,當(dāng)前保存的是任意 Bean 的信息,而后者,保存的是單例 Bean 的對(duì)象,我們將在下篇文章詳細(xì)討論。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 2.1 我們的理念是:讓別人為你服務(wù) IoC是隨著近年來輕量級(jí)容器(Lightweight Container)的...
    好好學(xué)習(xí)Sun閱讀 2,876評(píng)論 0 11
  • spring源碼分析(二) 目錄五、Spring 源碼解讀--5.1、什么是IOC/DI--5.2、Spring ...
    毛子果閱讀 487評(píng)論 0 0
  • 1、概述 ????spring的兩大核心:IOC(依賴注入)和AOP(面向切面),IOC本質(zhì)上就是一個(gè)線程安全的h...
    ALivn_3cf3閱讀 654評(píng)論 0 3
  • 最近有時(shí)間讀了一下Spring的源碼,順便記錄一下筆記,加深理解。關(guān)于Spring IoC源碼分析,網(wǎng)上有一篇文章...
    曾彪彪閱讀 914評(píng)論 0 3
  • 大港的雨似乎與別的不同,可能是多了幾分世間的煙火氣吧。大港這地已經(jīng)有好些日子不曾下雨了,近幾日可算是...
    春夜與鹿閱讀 183評(píng)論 2 1

友情鏈接更多精彩內(nèi)容