引用原文:
https://www.cmsblogs.com/article/1391375590933794816
[死磕 Spring 38/43] — IOC 之 BeanDefinition 注冊機:BeanDefinitionRegistry
正文
将定義 bean 的資源檔案解析成 BeanDefinition 後需要将其注入容器中,這個過程由 BeanDefinitionRegistry 來完成。
BeanDefinitionRegistry:向系統資料庫中注冊 BeanDefinition 執行個體,完成注冊的過程。
下圖是 BeanDefinitionRegistry 類結構圖:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiNx8FesU2cfdGLwczX0xiRGZkRGZ0Xy9GbvNGLwIzXlpXazxCeJpmT0sGVNBnVyQWQClGVF5UMR9Fd4VGdsATNfd3bkFGazxSUhxGatJGbwhFT1Y0Mk9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnLjZGZwMWZyQDNldjZjN2M4ETNhRTY4YmZ0EWOjVTNhhzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
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 容器的核心接口,其地位還是很重的。