將定義 bean 的資源檔案解析成 BeanDefinition 後需要將其註入容器中,這個過程由 BeanDefinitionRegistry 來完成。
BeanDefinitionRegistry:向登錄檔中註冊 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。
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(64);
他對註冊其中的 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);
}
}
其實上面一堆程式碼最重要就只有一句:
this.beanDefinitionMap.put(beanName, beanDefinition);
removeBeanDefinition()
其實也是呼叫 beanDefinitionMap.remove(beanName)
。
對於類 GenericApplicationContext ,檢視原始碼你會發現他實現註冊、登出功能都是委託 DefaultListableBeanFactory 實現的。
所以 BeanDefinition 註冊並不是非常高大上的功能,內部就是用一個 Map 實現 ,並不是多麼高大上的騷操作,所以有時候我們會潛意識地認為某些技術很高大上就覺得他很深奧,如果試著去一探究竟你會發現,原來這麼簡單。雖然 BeanDefinitionRegistry 實現簡單,但是它作為 Spring IOC 容器的核心介面,其地位還是很重的。