04-IOC之Bean的加載:ParentBeanFactory與依賴處理

1. 簡介

       如果從單例緩存中沒有獲取到單例bean,則說明兩種情況:

  1. beanscope 不是 singleton
  2. beanscopesingleton ,但是沒有初始化完成

       針對這兩種情況 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);
            }
        }
    }
}
// 省略很多代碼

       這段代碼主要處理如下幾個部分:

  1. 檢測。若當前 bean 在創建,則拋出 BeanCurrentlyInCreationException 異常。
  2. 如果 beanDefinitionMap 中不存在 beanNameBeanDefinition(即在 Spring bean初始化過程中沒有加載),則嘗試從 parentBeanFactory中加載。
  3. 判斷是否爲類型檢查。
  4. mergedBeanDefinitions 中獲取 beanName 對應的 RootBeanDefinition,如果這個 BeanDefinition是子 Bean 的話,則會合並父類的相關屬性。
  5. 依賴處理。

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 ,而原型模式的則是ThreadLocalprototypesCurrentlyInCreation 定義如下:

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);
    }
}

       整個過程較爲簡單,都是委託 parentBeanFactorygetBean() 進行處理,只不過在獲取之前對 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() 記錄:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章