歡迎光臨
每天分享高質量文章

【死磕 Spring】—– IOC 之 BeanDefinition 序號產生器

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

BeanDefinitionRegistry:向登錄檔中註冊 BeanDefinition 實體,完成註冊的過程。

下圖是 BeanDefinitionRegistry 類結構圖:

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

AliasRegistry

用於別名管理的通用型介面,作為 BeanDefinitionRegistry 的頂層介面。 AliasRegistry 定義了一些別名管理的方法。

  1. public interface AliasRegistry {
  2. void registerAlias(String name, String alias);
  3.  
  4. void removeAlias(String alias);
  5.  
  6. boolean isAlias(String name);
  7.  
  8. String[] getAliases(String name);
  9. }

BeanDefinitionRegistry

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

BeanDefinitionRegistry 介面定義了關於 BeanDefinition 註冊、登出、查詢等一系列的操作。

  1. public interface BeanDefinitionRegistry extends AliasRegistry {
  2.    // 往登錄檔中註冊一個新的 BeanDefinition 實體
  3. void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
  4.   throws BeanDefinitionStoreException;
  5.  
  6.    // 移除登錄檔中已註冊的 BeanDefinition 實體
  7. void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
  8.  
  9.    // 從註冊中取得指定的 BeanDefinition 實體
  10. BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
  11.  
  12.    // 判斷 BeanDefinition 實體是否在登錄檔中(是否註冊)
  13. boolean containsBeanDefinition(String beanName);
  14.  
  15.    // 取得登錄檔中所有 BeanDefinition 實體的 beanName(標識)
  16. String[] getBeanDefinitionNames();
  17.  
  18.    // 傳回登錄檔中 BeanDefinition 實體的數量
  19. int getBeanDefinitionCount();
  20.  
  21.    // beanName(標識)是否被佔用
  22. boolean isBeanNameInUse(String beanName);
  23. }

SimpleBeanDefinitionRegistry

SimpleBeanDefinitionRegistry 是 BeanDefinitionRegistry 一個簡單的實現,它還繼承 SimpleAliasRegistry( AliasRegistry 的簡單實現),它僅僅只提供註冊表功能,無工廠功能

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

  1. private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(64);

他對註冊其中的 BeanDefinition 都是基於 beanDefinitionMap 這個集合來實現的,如下:

  1. @Override
  2. public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
  3.   throws BeanDefinitionStoreException {
  4.  
  5.  Assert.hasText(beanName, "'beanName' must not be empty");
  6.  Assert.notNull(beanDefinition, "BeanDefinition must not be null");
  7.  this.beanDefinitionMap.put(beanName, beanDefinition);
  8. }
  9.  
  10. @Override
  11. public void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException {
  12.  if (this.beanDefinitionMap.remove(beanName) == null) {
  13.   throw new NoSuchBeanDefinitionException(beanName);
  14.  }
  15. }
  16.  
  17. @Override
  18. public BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException {
  19.  BeanDefinition bd = this.beanDefinitionMap.get(beanName);
  20.  if (bd == null) {
  21.   throw new NoSuchBeanDefinitionException(beanName);
  22.  }
  23.  return bd;
  24. }

實現簡單、粗暴。

DefaultListableBeanFactory

DefaultListableBeanFactory,ConfigurableListableBeanFactory(其實就是 BeanFactory ) 和 BeanDefinitionRegistry 介面的預設實現:一個基於 BeanDefinition 元資料的完整 bean 工廠。所以相對於 SimpleBeanDefinitionRegistry 而言,DefaultListableBeanFactory 則是一個具有註冊功能的完整 bean 工廠。它同樣是用 ConcurrentHashMap 資料結構來儲存註冊的 BeanDefinition。

  1. // 登錄檔,由 BeanDefinition 的標識 (beanName) 與其實體組成
  2. private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, bean>(64);
  3.  
  4. // 標識(beanName)集合
  5. private final List<String> beanDefinitionNames = new ArrayList<String>(64);

在看 registerBeanDefinition()

  1. public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
  2.   throws BeanDefinitionStoreException {
  3.  
  4.        // 省略其他程式碼
  5.  
  6.  else {
  7.   if (hasBeanCreationStarted()) {
  8.    // Cannot modify startup-time collection elements anymore (for stable iteration)
  9.    synchronized (this.beanDefinitionMap) {
  10.     this.beanDefinitionMap.put(beanName, beanDefinition);
  11.     List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
  12.     updatedDefinitions.addAll(this.beanDefinitionNames);
  13.     updatedDefinitions.add(beanName);
  14.     this.beanDefinitionNames = updatedDefinitions;
  15.     if (this.manualSingletonNames.contains(beanName)) {
  16.      Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
  17.      updatedSingletons.remove(beanName);
  18.      this.manualSingletonNames = updatedSingletons;
  19.     }
  20.    }
  21.   }
  22.   else {
  23.    // 註冊 BeanDefinition
  24.    this.beanDefinitionMap.put(beanName, beanDefinition);
  25.    this.beanDefinitionNames.add(beanName);
  26.    this.manualSingletonNames.remove(beanName);
  27.   }
  28.   this.frozenBeanDefinitionNames = null;
  29.  }
  30.  
  31.  if (existingDefinition != null || containsSingleton(beanName)) {
  32.   resetBeanDefinition(beanName);
  33.  }
  34. }

其實上面一堆程式碼最重要就只有一句:

  1. this.beanDefinitionMap.put(beanName, beanDefinition);

removeBeanDefinition() 其實也是呼叫 beanDefinitionMap.remove(beanName)

對於類 GenericApplicationContext ,檢視原始碼你會發現他實現註冊、登出功能都是委託 DefaultListableBeanFactory 實現的。

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

    贊(0)

    分享創造快樂