Spring原始碼系列:BeanDefinition載入(下)

glmapper發表於2019-03-01

Spring原始碼系列:BeanDefinition載入(上)中已經大概捋了一下解析過程,本篇將記錄一下bean的註冊過程。

bean的註冊就是DefaultListableBeanFactory中registerBeanDefinition方法來完成的。那我就來看registerBeanDefinition這個方法的具體邏輯。

1、beanDefinition型別判斷和驗證

這裡的驗證主要是驗證不能將靜態工廠方法與方法重寫相結合(靜態工廠方法必須建立例項);

if (beanDefinition instanceof AbstractBeanDefinition) {
try {
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(),
beanName,"Validation of bean definition failed", ex);
}
}
複製程式碼

2、嘗試從beanDefinitionMap中獲取老的bean

這裡就是先根據beanName從beanDefinitionMap去取BeanDefinition,並將結果給oldBeanDefinition。

BeanDefinition oldBeanDefinition;
oldBeanDefinition = this.beanDefinitionMap.get(beanName);
複製程式碼

3、beanDefinitionMap中已經存在名為beanName的Beandefinition

如果當前beanDefinitionMap中已經存在名為beanName的Beandefinition了(即檢查是否有相同名稱的beanDefinition已經在Ioc容器中註冊過了)。,如果有,則進行以下具體策略:

  • 如果不允許bean被覆蓋,則直接丟擲不能重新註冊,bean已經存在這樣的異常資訊
  • 使用框架生成的Bean來代替使用者自定義的bean
  • 覆蓋原有的Beandefinition
if (oldBeanDefinition != null) {
if (!isAllowBeanDefinitionOverriding()) {
//省略異常程式碼
}
else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
//省略異常程式碼
}
else if (!beanDefinition.equals(oldBeanDefinition)) {
//提示覆蓋log資訊
}
else {
//提示覆蓋log資訊
}
//覆蓋原有的Beandefinition
this.beanDefinitionMap.put(beanName, beanDefinition);
}
複製程式碼

4、beanDefinitionMap不存在名為beanName的Beandefinition

//檢查bean的建立階段是否已經開始,也就是說是否已經建立了
if (hasBeanCreationStarted()) {
//Cannot modify startup-time collection elements anymore (for stable iteration)
// 無法修改啟動時間收集元素(用於穩定迭代)(譯註)
//註冊過程需要保證資料的一致性,所有需要加鎖同步
synchronized (this.beanDefinitionMap) {
//註冊到beanDefinitionMap中
this.beanDefinitionMap.put(beanName, beanDefinition);
//下面就是將當前beanName存放到beanDefinitionNames中
List<String> updatedDefinitions = new ArrayList<String>(
this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
//如果單例模式的bean名單中有該bean的name,那麼移除掉它。
//也就是說著,將一個原本是單例模式的bean重新註冊成一個普通的bean
if (this.manualSingletonNames.contains(beanName)) {
Set<String> updatedSingletons = new
LinkedHashSet<String>(this.manualSingletonNames);
updatedSingletons.remove(beanName);
this.manualSingletonNames = updatedSingletons;
}
}
}
// 仍處於啟動階段,bean還沒有開始註冊
else {
// Still in startup registration phase
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
this.manualSingletonNames.remove(beanName);
}
this.frozenBeanDefinitionNames = null;
複製程式碼

5、執行快取清除

  • 1:oldBeanDefinition如果存在,且執行到了這裡也沒有丟擲異常,說明該BeanDefinition已經被覆蓋,快取需要更新。

  • 2:如果是單例模式的bean物件則Set中包含該beanName,執行到這裡說明該BeanDefinition已經從一個單例模式的bean變為了一個普通的bean,所以快取也需要更新。

if (oldBeanDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);
}
複製程式碼

OK,我們來看下resetBeanDefinition這個方法:

這個方法的作用就是重置給定bean的所有bean定義快取,包括從它派生的bean的快取。

protected void resetBeanDefinition(String beanName) {
// 如果已經建立,則刪除給定bean的合併bean定義。
clearMergedBeanDefinition(beanName);

// 如果有的話,從singleton 快取記憶體中刪除相應的bean。
//但是這也不是必須的,而只是為了覆蓋上下文的預設bean
//(就是從manualSingletonNames中移除)
destroySingleton(beanName);
//遞迴的方式來 重置具有給定bean作為父項的所有bean定義。
for (String bdName : this.beanDefinitionNames) {
if (!beanName.equals(bdName)) {
BeanDefinition bd = this.beanDefinitionMap.get(bdName);
if (beanName.equals(bd.getParentName())) {
resetBeanDefinition(bdName);
}
}
}
}
複製程式碼

Bean的註冊就到這裡了,下一篇學習的是DefaultListableBeanFactory這個集大成者容器。

相關文章