【死磕 Spring】----- IOC 之註冊解析的 BeanDefinition
DefaultBeanDefinitionDocumentReader.processBeanDefinition()
完成 Bean 標籤解析的核心工作,如下:
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired (ele, bdHolder);
try {
// Register the final decorated instance.
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder. getBeanName() + "'", ele, ex);
}
// Send registration event.
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
解析工作分為三步:1、解析預設標籤;2、解析預設標籤後下得自定義標籤;3、註冊解析後的 BeanDefinition。經過前面兩個步驟的解析,這時的 BeanDefinition 已經可以滿足後續的使用要求了,那麼接下來的工作就是將這些 BeanDefinition 進行註冊,也就是完成第三步。
註冊 BeanDefinition 由 BeanDefinitionReaderUtils.registerBeanDefinition()
完成。如下:
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// 註冊 beanName
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// 註冊 alias
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
首先通過 beanName 註冊 BeanDefinition ,然後再註冊別名 alias。BeanDefinition 的註冊由介面 BeanDefinitionRegistry 定義。
通過 beanName 註冊
BeanDefinitionRegistry.registerBeanDefinition()
實現通過 beanName 註冊 BeanDefinition,如下:
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
// 校驗 beanName 與 beanDefinition
Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
// 校驗 BeanDefinition
// 這是註冊前的最後一次校驗了,主要是對屬性 methodOverrides 進行校驗
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
BeanDefinition oldBeanDefinition;
// 從快取中獲取指定 beanName 的 BeanDefinition
oldBeanDefinition = this.beanDefinitionMap.get(beanName);
/**
* 如果存在
*/
if (oldBeanDefinition != null) {
// 如果存在但是不允許覆蓋,丟擲異常
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
"': There is already [" + oldBeanDefinition + "] bound.");
}
//
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 不是同類
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 + "]");
}
}
else {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Overriding bean definition for bean '" + beanName +
"' with an equivalent definition: replacing [" + oldBeanDefinition +
"] with [" + beanDefinition + "]");
}
}
// 允許覆蓋,直接覆蓋原有的 BeanDefinition
this.beanDefinitionMap.put(beanName, beanDefinition);
}
/**
* 不存在
*/
else {
// 檢測建立 Bean 階段是否已經開啟,如果開啟了則需要對 beanDefinitionMap 進行併發控制
if (hasBeanCreationStarted()) {
// beanDefinitionMap 為全域性變數,避免併發情況
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 {
// 不會存在併發情況,直接設定
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
this.manualSingletonNames.remove(beanName);
}
this.frozenBeanDefinitionNames = null;
}
if (oldBeanDefinition != null || containsSingleton(beanName)) {
// 重新設定 beanName 對應的快取
resetBeanDefinition(beanName);
}
}
處理過程如下:
- 首先 BeanDefinition 進行校驗,該校驗也是註冊過程中的最後一次校驗了,主要是對 AbstractBeanDefinition 的 methodOverrides 屬性進行校驗
- 根據 beanName 從快取中獲取 BeanDefinition,如果快取中存在,則根據 allowBeanDefinitionOverriding 標誌來判斷是否允許覆蓋,如果允許則直接覆蓋,否則丟擲 BeanDefinitionStoreException 異常
- 若快取中沒有指定 beanName 的 BeanDefinition,則判斷當前階段是否已經開始了 Bean 的建立階段(),如果是,則需要對 beanDefinitionMap 進行加鎖控制併發問題,否則直接設定即可。對於
hasBeanCreationStarted()
方法後續做詳細介紹,這裡不過多闡述。 - 若快取中存在該 beanName 或者 單利 bean 集合中存在該 beanName,則呼叫
resetBeanDefinition()
重置 BeanDefinition 快取。
其實整段程式碼的核心就在於 this.beanDefinitionMap.put(beanName, beanDefinition);
。BeanDefinition 的快取也不是神奇的東西,就是定義 map ,key 為 beanName,value 為 BeanDefinition。
註冊 alias
BeanDefinitionRegistry.registerAlias
完成 alias 的註冊。
public void registerAlias(String name, String alias) {
// 校驗 name 、 alias
Assert.hasText(name, "'name' must not be empty");
Assert.hasText(alias, "'alias' must not be empty");
synchronized (this.aliasMap) {
// name == alias 則去掉alias
if (alias.equals(name)) {
this.aliasMap.remove(alias);
}
else {
// 快取快取記錄
String registeredName = this.aliasMap.get(alias);
if (registeredName != null) {
// 快取中的相等,則直接返回
if (registeredName.equals(name)) {
// An existing alias - no need to re-register
return;
}
// 不允許則丟擲異常
if (!allowAliasOverriding()) {
throw new IllegalStateException("Cannot register alias '" + alias + "' for name '" +
name + "': It is already registered for name '" + registeredName + "'.");
}
}
// 當 A --> B 存在時,如果再次出現 A --> B --> C 則丟擲異常
checkForAliasCircle(name, alias);
// 註冊 alias
this.aliasMap.put(alias, name);
}
}
}
註冊 alias 和註冊 BeanDefinition 的過程差不多。在最好呼叫了 checkForAliasCircle()
來對別名進行了檢測。
public boolean hasAlias(String name, String alias) {
for (Map.Entry<String, String> entry : this.aliasMap.entrySet()) {
String registeredName = entry.getValue();
if (registeredName.equals(name)) {
String registeredAlias = entry.getKey();
return (registeredAlias.equals(alias) || hasAlias(registeredAlias, alias));
}
}
return false;
}
如果 name 、 alias 為 1 、3 ,則構成 (1,3),加入集合中存在(A,1)、(3,A)的情況則會出錯。
到這裡 BeanDefinition、alias 都已經注入到快取中,下一步則是等待初始化使用了。