spring深入學習(十六) IOC 之parentBeanFactory 與依賴處理
繼上篇部落格 【死磕 Spring】—– 載入 bean 之 快取中獲取單例 bean,如果從單例快取中沒有獲取到單例 bean,則說明兩種情況:
- 該 bean 的 scope 不是 singleton
- 該 bean 的 scope 是 singleton ,但是沒有初始化完成
針對這兩種情況 Spring 是如何處理的呢?統一載入並完成初始化!這部分內容的篇幅較長,拆分為兩部分,第一部分主要是一些檢測、parentBeanFactory 以及依賴處理,第二部分則是各個 scope 的初始化。
if (isPrototypeCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } // Check if bean definition exists in this factory. BeanFactory parentBeanFactory = getParentBeanFactory(); if (parentBeanFactory != null && !containsBeanDefinition(beanName)) { // Not found -> check parent. String nameToLookup = originalBeanName(name); if (parentBeanFactory instanceof AbstractBeanFactory) { return ((AbstractBeanFactory) parentBeanFactory).doGetBean( nameToLookup, requiredType, args, typeCheckOnly); } else if (args != null) { // Delegation to parent with explicit args. return (T) parentBeanFactory.getBean(nameToLookup, args); } else { // No args -> delegate to standard getBean method. return parentBeanFactory.getBean(nameToLookup, requiredType); } } if (!typeCheckOnly) { markBeanAsCreated(beanName); } try { final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); checkMergedBeanDefinition(mbd, beanName, args); // Guarantee initialization of beans that the current bean depends on. String[] dependsOn = mbd.getDependsOn(); if (dependsOn != null) { for (String dep : dependsOn) { if (isDependent(beanName, dep)) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'"); } registerDependentBean(dep, beanName); try { getBean(dep); } catch (NoSuchBeanDefinitionException ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "'" + beanName + "' depends on missing bean '" + dep + "'", ex); } } } } // 省略很多程式碼
這段程式碼主要處理如下幾個部分:
- 檢測。若當前 bean 在建立,則丟擲 BeanCurrentlyInCreationException 異常。
- 如果 beanDefinitionMap 中不存在 beanName 的 BeanDefinition(即在 Spring bean 初始化過程中沒有載入),則嘗試從 parentBeanFactory 中載入。
- 判斷是否為型別檢查。
- 從 mergedBeanDefinitions 中獲取 beanName 對應的 RootBeanDefinition,如果這個 BeanDefinition 是子 Bean 的話,則會合並父類的相關屬性。
- 依賴處理。
檢測
在前面就提過,Spring 只解決單例模式下的迴圈依賴,對於原型模式的迴圈依賴則是丟擲 BeanCurrentlyInCreationException 異常,所以首先檢查該 beanName 是否處於原型模式下的迴圈依賴。如下:
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
呼叫 isPrototypeCurrentlyInCreation()
判斷當前 bean 是否正在建立,如下:
protected boolean isPrototypeCurrentlyInCreation(String beanName) {
Object curVal = this.prototypesCurrentlyInCreation.get();
return (curVal != null &&
(curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>) curVal).contains(beanName))));
}
其實檢測邏輯和單例模式一樣,一個“集合”存放著正在建立的 bean,從該集合中進行判斷即可,只不過單例模式的“集合”為 Set ,而原型模式的則是 ThreadLocal,prototypesCurrentlyInCreation 定義如下:
private final ThreadLocal<Object> prototypesCurrentlyInCreation = new NamedThreadLocal<>("Prototype beans currently in creation");
檢查父類 BeanFactory
若 containsBeanDefinition 中不存在 beanName 相對應的 BeanDefinition,則從 parentBeanFactory 中獲取。
// 獲取 parentBeanFactory
BeanFactory parentBeanFactory = getParentBeanFactory();
// parentBeanFactory 不為空且 beanDefinitionMap 中不存該 name 的 BeanDefinition
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// 確定原始 beanName
String nameToLookup = originalBeanName(name);
// 若為 AbstractBeanFactory 型別,委託父類處理
if (parentBeanFactory instanceof AbstractBeanFactory) {
return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
nameToLookup, requiredType, args, typeCheckOnly);
}
else if (args != null) {
// 委託給建構函式 getBean() 處理
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else {
// 沒有 args,委託給標準的 getBean() 處理
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
}
整個過程較為簡單,都是委託 parentBeanFactory 的 getBean()
進行處理,只不過在獲取之前對 name 進行簡單的處理,主要是想獲取原始的 beanName,如下:
protected String originalBeanName(String name) {
String beanName = transformedBeanName(name);
if (name.startsWith(FACTORY_BEAN_PREFIX)) {
beanName = FACTORY_BEAN_PREFIX + beanName;
}
return beanName;
}
transformedBeanName()
是對 name 進行轉換,獲取真正的 beanName,因為我們傳遞的可能是 aliasName(這個過程在部落格 【死磕 Spring】—– 載入 bean 之 開啟 bean 的載入 中分析 transformedBeanName()
有詳細說明),如果 name 是以 “&” 開頭的,則加上 “&”,因為在 transformedBeanName()
將 “&” 去掉了,這裡補上。
型別檢查
引數 typeCheckOnly 是用來判斷呼叫 getBean()
是否為型別檢查獲取 bean。如果不是僅僅做型別檢查則是建立bean,則需要呼叫 markBeanAsCreated()
記錄:
protected void markBeanAsCreated(String beanName) {
// 沒有建立
if (!this.alreadyCreated.contains(beanName)) {
// 加上全域性鎖
synchronized (this.mergedBeanDefinitions) {
// 再次檢查一次:DCL 雙檢查模式
if (!this.alreadyCreated.contains(beanName)) {
// 從 mergedBeanDefinitions 中刪除 beanName,
// 並在下次訪問時重新建立它。
clearMergedBeanDefinition(beanName);
// 新增到已建立bean 集合中
this.alreadyCreated.add(beanName);
}
}
}
}
獲取 RootBeanDefinition
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
呼叫 getMergedLocalBeanDefinition()
獲取相對應的 BeanDefinition,如下:
protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {
// 快速從快取中獲取,如果不為空,則直接返回
RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);
if (mbd != null) {
return mbd;
}
// 獲取 RootBeanDefinition,
// 如果返回的 BeanDefinition 是子類 bean 的話,則合併父類相關屬性
return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));
}
首先直接從 mergedBeanDefinitions 快取中獲取相應的 RootBeanDefinition,如果存在則直接返回否則呼叫 getMergedBeanDefinition()
獲取 RootBeanDefinition,若獲取的 BeanDefinition 為子 BeanDefinition,則需要合併父類的相關屬性。
處理依賴
如果一個 bean 有依賴 bean 的話,那麼在初始化該 bean 時是需要先初始化它所依賴的 bean。
// 獲取依賴。
// 在初始化 bean 時解析 depends-on 標籤時設定
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
// 迭代依賴
for (String dep : dependsOn) {
// 檢驗依賴的bean 是否已經註冊給當前 bean 獲取其他傳遞依賴bean
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
// 註冊到依賴bean中
registerDependentBean(dep, beanName);
try {
// 呼叫 getBean 初始化依賴bean
getBean(dep);
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
}
}
}
這段程式碼邏輯是:通過迭代的方式依次對依賴 bean 進行檢測、校驗,如果通過則呼叫 getBean()
例項化依賴 bean。
isDependent()
是校驗該依賴是否已經註冊給當前 bean。
protected boolean isDependent(String beanName, String dependentBeanName) {
synchronized (this.dependentBeanMap) {
return isDependent(beanName, dependentBeanName, null);
}
}
同步加鎖給 dependentBeanMap 物件,然後呼叫 isDependent()
校驗。dependentBeanMap 物件儲存的是依賴 beanName 之間的對映關係:beanName – > 依賴 beanName 的集合
private boolean isDependent(String beanName, String dependentBeanName, @Nullable Set<String> alreadySeen) {
// alreadySeen 已經檢測的依賴 bean
if (alreadySeen != null && alreadySeen.contains(beanName)) {
return false;
}
// 獲取原始 beanName
String canonicalName = canonicalName(beanName);
// 獲取當前 beanName 的依賴集合
Set<String> dependentBeans = this.dependentBeanMap.get(canonicalName);
// 不存在依賴,返回false
if (dependentBeans == null) {
return false;
}
// 存在,則證明存在已經註冊的依賴
if (dependentBeans.contains(dependentBeanName)) {
return true;
}
// 遞迴檢測依賴
for (String transitiveDependency : dependentBeans) {
if (alreadySeen == null) {
alreadySeen = new HashSet<>();
}
alreadySeen.add(beanName);
if (isDependent(transitiveDependency, dependentBeanName, alreadySeen)) {
return true;
}
}
return false;
}
如果校驗成功,則呼叫 registerDependentBean()
將該依賴進行註冊,便於在銷燬 bean 之前對其進行銷燬。
public void registerDependentBean(String beanName, String dependentBeanName) {
String canonicalName = canonicalName(beanName);
synchronized (this.dependentBeanMap) {
Set<String> dependentBeans =
this.dependentBeanMap.computeIfAbsent(canonicalName, k -> new LinkedHashSet<>(8));
if (!dependentBeans.add(dependentBeanName)) {
return;
}
}
synchronized (this.dependenciesForBeanMap) {
Set<String> dependenciesForBean =
this.dependenciesForBeanMap.computeIfAbsent(dependentBeanName, k -> new LinkedHashSet<>(8));
dependenciesForBean.add(canonicalName);
}
}
其實將就是該對映關係儲存到兩個集合中:dependentBeanMap、dependenciesForBeanMap。
最後呼叫 getBean()
例項化依賴 bean。
至此,載入 bean 的第二個部分也分析完畢了,下篇開始分析第三個部分:各大作用域 bean 的處理