天天看點

[死磕 Spring 38/43] --- IOC 之 BeanDefinition 注冊機:BeanDefinitionRegistry

引用原文:

https://www.cmsblogs.com/article/1391375590933794816

[死磕 Spring 38/43] — IOC 之 BeanDefinition 注冊機:BeanDefinitionRegistry

正文

将定義 bean 的資源檔案解析成 BeanDefinition 後需要将其注入容器中,這個過程由 BeanDefinitionRegistry 來完成。

BeanDefinitionRegistry:向系統資料庫中注冊 BeanDefinition 執行個體,完成注冊的過程。

下圖是 BeanDefinitionRegistry 類結構圖:

[死磕 Spring 38/43] --- IOC 之 BeanDefinition 注冊機:BeanDefinitionRegistry

BeanDefinitionRegistry 繼承了 AliasRegistry 接口,其核心子類有三個:SimpleBeanDefinitionRegistry、DefaultListableBeanFactory、GenericApplicationContext。

AliasRegistry

用于别名管理的通用型接口,作為 BeanDefinitionRegistry 的頂層接口。 AliasRegistry 定義了一些别名管理的方法。

public interface AliasRegistry {
     void registerAlias(String name, String alias);
    
     void removeAlias(String alias);
    
     boolean isAlias(String name);
    
     String[] getAliases(String name);
    }
           

BeanDefinitionRegistry

BeanDefinition 的注冊接口,如 RootBeanDefinition 和 ChildBeanDefinition。它通常由 BeanFactories 實作,在 Spring 中已知的實作者為:DefaultListableBeanFactory 和 GenericApplicationContext。BeanDefinitionRegistry 是 Spring 的 Bean 工廠包中唯一封裝 BeanDefinition 注冊的接口。

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;
    
        // 判斷 BeanDefinition 執行個體是否在系統資料庫中(是否注冊)
     boolean containsBeanDefinition(String beanName);
    
        // 取得系統資料庫中所有 BeanDefinition 執行個體的 beanName(辨別)
     String[] getBeanDefinitionNames();
    
        // 傳回系統資料庫中 BeanDefinition 執行個體的數量
     int getBeanDefinitionCount();
    
        // beanName(辨別)是否被占用
     boolean isBeanNameInUse(String beanName);
    }
           

SimpleBeanDefinitionRegistry

SimpleBeanDefinitionRegistry 是 BeanDefinitionRegistry 一個簡單的實作,它還繼承 SimpleAliasRegistry( AliasRegistry 的簡單實作),它僅僅隻提供系統資料庫功能,無工廠功能。

SimpleBeanDefinitionRegistry 使用 ConcurrentHashMap 來存儲注冊的 BeanDefinition。

他對注冊其中的 BeanDefinition 都是基于 beanDefinitionMap 這個集合來實作的,如下:

@Override
     public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
       throws BeanDefinitionStoreException {
    
      Assert.hasText(beanName, "'beanName' must not be empty");
      Assert.notNull(beanDefinition, "BeanDefinition must not be null");
      this.beanDefinitionMap.put(beanName, beanDefinition);
     }
    
     @Override
     public void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException {
      if (this.beanDefinitionMap.remove(beanName) == null) {
       throw new NoSuchBeanDefinitionException(beanName);
      }
     }
    
     @Override
     public BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException {
      BeanDefinition bd = this.beanDefinitionMap.get(beanName);
      if (bd == null) {
       throw new NoSuchBeanDefinitionException(beanName);
      }
      return bd;
     }
           

實作簡單、粗暴。

DefaultListableBeanFactory

DefaultListableBeanFactory,ConfigurableListableBeanFactory(其實就是 BeanFactory ) 和 BeanDefinitionRegistry 接口的預設實作:一個基于 BeanDefinition 中繼資料的完整 bean 工廠。是以相對于 SimpleBeanDefinitionRegistry 而言,DefaultListableBeanFactory 則是一個具有注冊功能的完整 bean 工廠。它同樣是用 ConcurrentHashMap 資料結構來存儲注冊的 BeanDefinition。

// 系統資料庫,由 BeanDefinition 的辨別 (beanName) 與其執行個體組成
    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, bean>(64);
    
    // 辨別(beanName)集合
    private final List<String> beanDefinitionNames = new ArrayList<String>(64);
           

再看 registerBeanDefinition():

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
       throws BeanDefinitionStoreException {
    
            // 省略其他代碼
    
      else {
       if (hasBeanCreationStarted()) {
        // Cannot modify startup-time collection elements anymore (for stable iteration)
        synchronized (this.beanDefinitionMap) {
         this.beanDefinitionMap.put(beanName, beanDefinition);
         List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
         updatedDefinitions.addAll(this.beanDefinitionNames);
         updatedDefinitions.add(beanName);
         this.beanDefinitionNames = updatedDefinitions;
         if (this.manualSingletonNames.contains(beanName)) {
          Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
          updatedSingletons.remove(beanName);
          this.manualSingletonNames = updatedSingletons;
         }
        }
       }
       else {
        // 注冊 BeanDefinition
        this.beanDefinitionMap.put(beanName, beanDefinition);
        this.beanDefinitionNames.add(beanName);
        this.manualSingletonNames.remove(beanName);
       }
       this.frozenBeanDefinitionNames = null;
      }
    
      if (existingDefinition != null || containsSingleton(beanName)) {
       resetBeanDefinition(beanName);
      }
     }
           

其實上面一堆代碼最重要就隻有一句:

removeBeanDefinition() 其實也是調用 beanDefinitionMap.remove(beanName)。

對于類 GenericApplicationContext ,檢視源碼你會發現他實作注冊、登出功能都是委托 DefaultListableBeanFactory 實作的。

是以 BeanDefinition 注冊并不是非常高大上的功能,内部就是用一個 Map 實作 ,并不是多麼高大上的騷操作,是以有時候我們會潛意識地認為某些技術很高大上就覺得他很深奧,如果試着去一探究竟你會發現,原來這麼簡單。雖然 BeanDefinitionRegistry 實作簡單,但是它作為 Spring IOC 容器的核心接口,其地位還是很重的。