ParentBeanFactory與依賴處理
1. 簡介
如果從單例緩存中沒有獲取到單例bean
,則說明兩種情況:
- 該
bean
的scope
不是singleton
- 該
bean
的scope
是singleton
,但是沒有初始化完成
針對這兩種情況 Spring
是如何處理的呢?統一加載並完成初始化!
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 的話,則會合並父類的相關屬性。 - 依賴處理。
2. 檢測
在前面就提過,Spring
只解決單例模式下的循環依賴,對於原型模式的循環依賴則是拋出BeanCurrentlyInCreationException
異常,所以首先檢查該 beanName
是否處於原型模式下的循環依賴。如下:
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
調用 isPrototypeCurrentlyInCreation()
判斷當前 bean
是否正在創建,如下:
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");
3. 檢測父類 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
,如果 name
是以 “&” 開頭的,則加上 “&”,因爲在 transformedBeanName()
將 “&” 去掉了,這裏補上。
4. 類型檢查
參數typeCheckOnly
是用來判斷調用 getBean()
是否爲類型檢查獲取 bean
。如果不是僅僅做類型檢查則是創建bean
,則需要調用 markBeanAsCreated()
記錄: