1. 程式人生 > >Spring(五)核心容器 - 註冊 Bean、BeanDefinitionRegistry 簡介

Spring(五)核心容器 - 註冊 Bean、BeanDefinitionRegistry 簡介

目錄

  • 前言
  • 正文
    • 1、BeanDefinitionRegistry 簡介
    • 2、registerBeanDefinition 方法註冊 Bean
  • 最後

前言

上篇文章我們對 BeanDefinition 進行了討論,BeanDefinition 是對 Bean 的定義,其儲存了 Bean 的各種資訊,如屬性、構造方法引數、是否單例、是否延遲載入等。這裡的註冊 Bean 是指將 Bean 定義成 BeanDefinition,之後放入 Spring 容器中,我們常說的容器其實就是 Beanfactory 中的一個 Map,key 是 Bean 的名稱,value 是 Bean 對應的 BeanDefinition,這個註冊 Bean 的方法由 BeanFactory 子類實現。

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

正文

在前面的《Spring(三)核心容器 - ApplicationContext 上下文啟動準備》文章中說過,當前環境的 BeanFactory 實現類是 DefaultListableBeanFactory,是一個具有註冊功能的完整 Bean 工廠,註冊 Bean 的方法是 registerBeanDefinition,DefaultListableBeanFactory 通過實現 BeanDefinitionRegistry 介面,重寫該方法。

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

    ...

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

討論 registerBeanDefinition 方法之前,先來簡單介紹 BeanDefinitionRegistry 介面。

1、BeanDefinitionRegistry 簡介

BeanDefinitionRegistry 是一個介面,它定義了關於 BeanDefinition 的註冊、移除、查詢等一系列的操作。

public interface BeanDefinitionRegistry extends AliasRegistry {

    // 註冊 BeanDefinition
    void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException;

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

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

    // 根據 beanName 判斷容器是否存在對應的 BeanDefinition 
    boolean containsBeanDefinition(String beanName);

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

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

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

該介面有三個實現類:DefaultListableBeanFactory、GenericApplicationContext、SimpleBeanDefinitionRegistry,其中 GenericApplicationContext 底層呼叫的是 DefaultListableBeanFactory 中的實現方法,所以嚴格意義上來說,只有兩個實現類。這裡,我們主要討論 DefaultListableBeanFactory 中的方法實現。

2、registerBeanDefinition 方法註冊 Bean

前面說過 registerBeanDefinition 方法的主要實現類是 DefaultListableBeanFactory :

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
    
    ...
    
    // 儲存所有的 BeanDefinition ,key 是 Bean 的名稱。我們一直稱呼的容器,底層就是這個 Map
    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
    // 儲存所有 Bean 名稱
    private volatile List<String> beanDefinitionNames = new ArrayList<>(256);
    // 儲存手動註冊的單例 Bean 名稱
    private volatile Set<String> manualSingletonNames = new LinkedHashSet<>(16);
    // 儲存凍結的 BeanDefinition,留作後面快取用
    private volatile String[] frozenBeanDefinitionNames;
    
    ...
    
    // 方法的入參為 Bean 名稱和對應的 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 的例項為 AbstractBeanDefinition,則進行驗證
        if (beanDefinition instanceof AbstractBeanDefinition) {
            try {
                // 驗證:
                //     如果有重寫方法,但是是工廠方法,則丟擲異常,因為重寫方法需要代理,而工廠方法無法代理;
                //     通過方法名稱,判斷 Bean 中該名稱方法存在的數量,0:方法不存在,報錯;1:方法非過載,overloaded 屬性設為 false;
                ((AbstractBeanDefinition) beanDefinition).validate();
            }
            catch (BeanDefinitionValidationException ex) {
                throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                        "Validation of bean definition failed", ex);
            }
        }

        BeanDefinition oldBeanDefinition;
        // 先從 beanDefinitionMap 中嘗試獲取 beanName 對應 BeanDefinition
        oldBeanDefinition = this.beanDefinitionMap.get(beanName);
        
        // 不為 null,則 beanName 對應的 BeanDefinition 已經存在
        if (oldBeanDefinition != null) {
            
            // 是否應允許覆蓋 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 內部的 Bean;
            // 當原 BeanDefinition 角色小於新的 BeanDefinition 角色時,輸出一個 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 + "]");
                }
            }
            // 當新 BeanDefinition 屬性值不等於原 BeanDefinition 屬性值時,輸出 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 中無對應的 BeanDefinition,則直接註冊
        else {
            // 已開始建立 Bean 
            if (hasBeanCreationStarted()) {
                synchronized (this.beanDefinitionMap) {
                    // 將 Bean 對應的 BeanDefinition 放入 beanDefinitionMap 中
                    this.beanDefinitionMap.put(beanName, beanDefinition);
                    
                    // 建立新的 beanNames 集合,並將已快取的 beanName 和新的 beanName 加入該集合
                    List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
                    updatedDefinitions.addAll(this.beanDefinitionNames);
                    updatedDefinitions.add(beanName);
                    this.beanDefinitionNames = updatedDefinitions;
                    
                    // 在手動註冊 Bean 的集合中,如果存在同名的 beanName,則將集合中同名的 beanName 刪除
                    if (this.manualSingletonNames.contains(beanName)) {
                        Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
                        updatedSingletons.remove(beanName);
                        this.manualSingletonNames = updatedSingletons;
                    }
                }
            }
            // 仍處於啟動註冊階段
            else {
                // 將當前 Bean 對應的 BeanDefinition 放入 beanDefinitionMap 中
                this.beanDefinitionMap.put(beanName, beanDefinition);
                // 將當前 beanName 放入 beanDefinitionNames
                this.beanDefinitionNames.add(beanName);
                // 刪除手動註冊 Bean 集合中同名的 beanName
                this.manualSingletonNames.remove(beanName);
            }
            
            // 將儲存凍結 BeanDefinition 的 Map 置為 null
            this.frozenBeanDefinitionNames = null;
        }

        // 當前註冊的 BeanDefinition 已在 beanDefinitionMap 中存在,或者其例項已在儲存單例 Bean 的 Map 中存在
        if (oldBeanDefinition != null || containsSingleton(beanName)) {
            
            // 重置 BeanDefinition,主要做一些清理工作
            resetBeanDefinition(beanName);
        }
    }
}

執行完 registerBeanDefinition 方法後,Bean 的名稱和對應的 BeanDefinition 就被放入了容器中,後續獲取 Bean 也是從這個容器中獲取。

當然,DefaultListableBeanFactory 還實現了 BeanDefinitionRegistry 介面的其它方法,如對 BeanDefinition 進行移除、判斷是否存在、獲取數量等操作,其實都是圍繞 beanDefinitionMap 這個 Map 進行的,這裡就不詳細介紹。

需要注意的是 registerBeanDefinition 方法會在後面頻繁被呼叫,後續會逐一提到。

最後

這篇文章主要對註冊 Bean 的核心方法進行討論,但其中涉及到了單例 Bean 例項註冊,這和當前文章的註冊 Bean 不同,當前儲存的是任意 Bean 的資訊,而後者,儲存的是單例 Bean 的物件,我們將在下篇文章詳細討