【Spring源碼】IOC實現-bean加載

1、轉換對應 beanName

爲什麼需要將傳入的參數 name 轉換成對應的 beanName?
  傳入的參數 name 就是 beanName,可能是別名,可能是FactoryBean

怎麼轉換?

  1. 去除 FactoryBean的修飾符,比如 name="&aa"–> name=“aa”
  2. 取出指定 alias 所表示的最終 beanName,例如 A 的別名指向 B,B 指向 C,則返回 C

2、緩存中獲取單例 bean

這是什麼意思?
  單例在spring的同一個容器內只會被創建一次,後續再獲取bean,就直接從單例緩存中獲取。 這裏是嘗試加載,首先嚐試從緩存中加載,如果加載不成功則再次嘗試從singletonFactories中加載。

爲什麼要這麼做?
  在創建單例bean的時候會存在依賴注入的情況,而在創建依賴的時候爲了避免循環依賴,在spring中創建bean的原則是不等bean創建完成 就會將創建bean的ObjectFactory提早加入到緩存中,一旦下一個bean創建需要依賴上一個bean則直接使用ObjectFactory

源碼實現

過程總結

  1. 嘗試從singletonObjects裏獲取實例
  2. 如果獲取不到再從earlySingleton-Objects裏面獲取
  3. 如果還獲取不到,再嘗試從singletonFactories裏面獲取beanName對應的ObjectFactory
  4. 調用這個ObjectFactory的getObject來創建bean
  5. 放到earlySingleton-Objects裏
  6. 從singletonFacotories裏面remove掉這個ObjectFactory
@Override
@Nullable
public Object getSingleton(String beanName) {
    //參數true是允許早期依賴
    return getSingleton(beanName, true);
}
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    //檢查緩存中是否存在實例
    Object singletonObject = this.singletonObjects.get(beanName);
    //如果緩存爲空,且單例bean正在創建中,則鎖定全局變量
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            //如果此bean正在加載,則不處理
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
                //當某些方法需要提前初始化的時候會直接調用addSingletonFactory把對應的ObjectFactory初始化策略存儲在singletonFactory中
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    //調用預先設定的getObject方法
                    singletonObject = singletonFactory.getObject();
                    //記錄在緩存中,注意earlySingletonObjects和singletonFactories是互斥的
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}

3、從 bean 的實例中獲取對象

得到 bean 的實例後,我們需要調用 getObjectForBeanInstance 方法來檢測正確性(檢測當前 bean 是否是 FactoryBean 類型的 bean ),如果是,調用該bean對應的FactoryBean實例中的getObject()作爲返回值。
  getObjectForBeanInstance 方法的過程總結:

  1. 對 FactoryBean 正確性的驗證
  2. 對非 FactoryBean 不做任何處理
  3. 對 bean 進行轉換(將存儲 XML 配置文件的 GernericBeanDefinition 轉換爲 RootBeanDefinition)
  4. 將從 Factory 中解析 bean 的工作委託給 getObjectFromFactoryBean

代碼

protected Object getObjectForBeanInstance(Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {

	// 如果指定的 name 是工廠相關(以&爲前綴),且 beanInstance 不是 FactoryBean 類型則驗證不通過
	if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {
		throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
	}

	// 現在我們有個 bean 的實例,這個實例可能會是正常的 bean 或者是 FactoryBean
	// 如果是 FactoryBean 我們使用它創建實例,但如果用戶想直接獲取工廠實例而不是工廠的 getObject 對應的實例
	// 那麼傳入的 name應該加入前綴 &
	if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
		return beanInstance;
	}

	// 加載 FactoryBean
	Object object = null;
	if (mbd == null) {
		//從緩存中加載 Bean
		object = getCachedObjectForFactoryBean(beanName);
	}
	//讓Bean工廠生產給定名稱的Bean對象實例
	if (object == null) {
		// 到這裏已經明確知道 beanInstance 一定是 FactoryBean 類型
		FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
		//在所有已經加載的類中檢測是否定義 beanName
		if (mbd == null && containsBeanDefinition(beanName)) {
			//將存儲 XML 配置文件的 GenericBeanDefinition 轉換爲 RootBeanDefinition
			//從容器中獲取指定名稱的Bean定義,如果繼承基類,則合併基類相關屬性
			mbd = getMergedLocalBeanDefinition(beanName);
		}
		//是否是用戶定義而不是應用程序本身定義的
		boolean synthetic = (mbd != null && mbd.isSynthetic());
		object = getObjectFromFactoryBean(factory, beanName, !synthetic);
	}
	return object;
}

4、獲取單例

過程

(1)檢查緩存是否已經加載過
(2)如果沒有加載,則記錄beanName的正在加載狀態
(3)加載單例前記錄加載狀態。

可能你會覺得beforeSingletonCreation方法是個空實現,裏面沒有任何邏輯,但其實這個函數中做了一個很重要的操作:記錄加載狀態,也就是通過this.singletonsCurrentlyInCreation.add(beanName)將當前正要創建的bean記錄在緩存中,這樣便可以對循環依賴進行檢測。

(4)通過調用參數傳入的ObjectFactory的個體Object方法實例化bean
(5)加載單例後的處理方法調用

同步驟3的記錄加載狀態相似,當bean加載結束後需要移除緩存中對該bean的正在加載狀態的記錄。

(6)將結果記錄至緩存並刪除加載bean過程中所記錄的各種輔助狀態
(7)返回處理結果

源碼

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(beanName, "Bean name must not be null");
    // 全局變量需要同步
    synchronized (this.singletonObjects) {
        // 首先檢查對應的bean是否已經加載過,因爲singleton模式其實就是複用已創建的bean
        Object singletonObject = this.singletonObjects.get(beanName);
        // 如果爲空,纔可以進行singleton的bean的初始化
        if (singletonObject == null) {
            if (this.singletonsCurrentlyInDestruction) {
                throw new BeanCreationNotAllowedException(beanName,
                        "Singleton bean creation not allowed while singletons of this factory are in destruction " +
                        "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
            }
            if (logger.isDebugEnabled()) {
                logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
            }
            beforeSingletonCreation(beanName);
            boolean newSingleton = false;
            boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
            if (recordSuppressedExceptions) {
                this.suppressedExceptions = new LinkedHashSet<>();
            }
            try {
                // 初始化bean
                singletonObject = singletonFactory.getObject();
                newSingleton = true;
            }
            catch (IllegalStateException ex) {
                // Has the singleton object implicitly appeared in the meantime ->
                // if yes, proceed with it since the exception indicates that state.
                singletonObject = this.singletonObjects.get(beanName);
                if (singletonObject == null) {
                    throw ex;
                }
            }
            catch (BeanCreationException ex) {
                if (recordSuppressedExceptions) {
                    for (Exception suppressedException : this.suppressedExceptions) {
                        ex.addRelatedCause(suppressedException);
                    }
                }
                throw ex;
            }
            finally {
                if (recordSuppressedExceptions) {
                    this.suppressedExceptions = null;
                }
                afterSingletonCreation(beanName);
            }
            if (newSingleton) {
                // 加入緩存
                addSingleton(beanName, singletonObject);
            }
        }
        return singletonObject;
    }
}

5、準備創建 bean

過程
(1)設置class屬性或者根據className來解析class
(2)驗證MethodOverride屬性進行驗證及標記

在上述代碼中有一個方法:mbd.prepareMethodOverrides();
其實在Spring中確實沒有override-method這樣的配置,但是我們前面說過,在Spring配置中是存在lookup-method和replace-method的,而這個兩個配置的加載其實就是將配置統一存放在BeanDefinition中的methodOverrides屬性裏,而這個函數的操作也就是針對於這兩個配置的。

(3)應用初始化前的後處理器,解析指定bean是否存在應用初始化前的短路操作
(4)創建bean

代碼

@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
    if (logger.isDebugEnabled()) {
        logger.debug("Creating instance of bean '" + beanName + "'");
    }
    RootBeanDefinition mbdToUse = mbd;
    
    // 鎖定class,根據設置的class屬性或者根據className來解析class
    Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
    if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
        mbdToUse = new RootBeanDefinition(mbd);
        mbdToUse.setBeanClass(resolvedClass);
    }
    // 驗證及準備覆蓋的方法
    try {
        // 處理override屬性
        mbdToUse.prepareMethodOverrides();
    }
    catch (BeanDefinitionValidationException ex) {throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),beanName, "Validation of method overrides failed", ex);}
    try {
        // 給 BeanPostProcessors 一個機會來返回代理來替代真正的實例(實例化的前置處理)
        Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
        if (bean != null) {
            return bean;
        }
    }
    catch (Throwable ex) { throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,"BeanPostProcessor before instantiation of bean failed", ex);}
    try {
        Object beanInstance = doCreateBean(beanName, mbdToUse, args);
        if (logger.isDebugEnabled()) {
            logger.debug("Finished creating instance of bean '" + beanName + "'");
        }
        return beanInstance;
    }
    catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { throw ex; }
    catch (Throwable ex) {throw new BeanCreationException( mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);}
}

6、循環依賴

循環依賴 vs 循環調用

循環依賴:A 引用 B,B 引用 C,C 引用 A(循環引用)
循環調用:方法間的環調用,無法解決,除非有終結條件,否則就是死循環,最終導致內存溢出。

Spring 如何解決循環依賴

1、構造器

無法解決,只能拋異常(BeanCurrentlyInCreationException)

如在創建TestA類時,構造器需要TestB類,那將去創建TestB,在創建TestB類時又發現
需要TestC類,則又去創建TestC,最終在創建TestC時發現又需要TestA,從而形成一個環,
沒辦法創建。
  spring容器將每一個正在創建的bean標識符放在一個“當前創建bean池"中,bean標識
符在創建過程中將一直保持在這個池中,因此如果在創建bean過程中發現自己已經在“當前
創建bean池”裏時,將拋出BeanCurrentlyInCreationException異常表示循環依賴;而對於創建完畢的bean將從“當前創建bean池”中清除掉。

測試

<bean id="testA" class="com.bean.TestA">
	<constructor-arg index="0" ref="testB"/> 
</bean>
<bean id="testB" class="com.bean.TestB">
	<constructor-arg index="0" ref="testC"/> 
</bean>
<bean id="testC" class="com.bean.TestC">
	<constructor-arg index="0" ref="testA"/> 
</bean>

過程
1、Spring容器創建"testA”bean,首先去“當前創建bean池”查找是否當前bean正在創建,如果沒發現,則繼續準備其需要的構造器參數"testB”,並將"testA”標識符放到“當前創建bean池”。
2、Spring容器創建"testB”bean,首先去“當前創建bean池"查找是否當前bean正在創建,如果沒發現,則繼續準備其需要的構造器參數"testc”,並將"testB”標識符放到“當前創建bean池”。
3、Spring容器創建"testc”bean,首先去“當前創建bean池”查找是否當前bean正在創建,如果沒發現,則繼續準備其需要的構造器參數"testA”,並將"testC”標識符放到“當前創建bean池”。
4、到此爲止Spring容器要去創建"testA”bean,發現該bean標識符在“當前創建bean
池”中,因爲表示循環依賴,拋出BeanCurrentlyInCreationException。

2、setter

what:通過Spring容器提前暴露剛完成構造器注入但未完成其他步驟(如setter注入)的bean來完成的

how:解決單例作用域的bean循環依賴。通過提前暴露一個單例工廠方法,從而使其他bean能引用到該bean。

具體步驟:
1、Spring容器創建單例"testA”bean,首先根據無參構造器創建bean,並暴露一個"ObjectFactory”用於返回一個提前暴露一個創建中的bean,並將"testA”標識符放到“當前創建bean池”,然後進行setter注入"testB”
2、Spring容器創建單例"testB”bean,首先根據無參構造器創建bean,並暴露一個"ObjectFactory”用於返回一個提前暴露一個創建中的bean,並將"testB”標識符放到“當前創建bean池”,然後進行setter注入"circle”
3、Spring容器創建單例"testC”bean,首先根據無參構造器創建bean,並暴露一個"ObjectFactory”用於返回一個提前暴露一個創建中的bean,並將"testc”標識符放到“當前創建bean池",然後進行setter注入“testA”。進行注入"testA”時由於提前暴露了"ObjectFactory"工廠,從而使用它返回提前暴露一個創建中的bean。
4、最後在依賴注入"testB”和"testA”,完成setter注入。

3、prototype範圍的依賴處理

對於 “prototype” 作用域 bean,Spring 容器無法完成依賴注入,因爲Spring容器不進行緩存 “prototype” 作用域的 bean,因此無法提前暴露一個創建中的 bean 。
對於 “singleton” 作用域 bean,可以通過 "setAIIowCircuIarReferences(false);” 來禁用循環引用。

7、創建 bean

7.1 創建 bean 的實例(實例化)

實例化邏輯

  1. 根據 RootBeanDefinition 中的配置生成 bean 的實例
  2. 解析構造函數
  3. 進行構造函數的實例化

Spring 在根據參數和類型去判斷最終會使用哪個構造函數進行實例化,但判斷過程比較耗性能,因此採用緩存機制

實例化的兩種情況:
1.通用的實例化(instantiateBean)
  直接調用實例化策略
2.帶有參數的實例化(autowireConstructor)
  2.1 構造函數參數的確定
    2.1.1 根據參數 explicitArgs 判斷(不爲空,即爲構造函數的參數)
    2.1.2 緩存中獲取(緩存中的參數可能是初始類型,也可能是最終類型,因此要經過類型轉換器的過濾)
    2.1.3 配置文件獲取(信息保存在 BeanDefinition)
  2.2 構造函數的確定(構造函數、參數名稱、參數類型、參數值)
(匹配對應構造函數的方法:參數個數)
    2.2.1 按照 public 、非 public 構造函數的參數數量排序
    2.2.2 獲取參數名稱:註解、工具類 ParameterNameDiscoverer
  2.3 根據確定的構造函數轉換對應的參數類型(類型轉換器:Spring 提供、用戶自定義)
  2.4 構造函數不確定性的驗證(父子關係情況)
  2.5 根據 實例化策略 + 得到的構造函數 + 構造函數參數 --> 實例化 Bean

2.1、2.2 是 基礎條件,2.5是組合

實例化策略:
1-用戶沒有使用需要動態改變的方法(replace、lookup),則使用反射的方式
2-否則,使用動態代理方式,將包含兩個特性所對應的邏輯的攔截增強器社設置進去

7.2 記錄創建 bean 的 ObjectFactory(處理循環依賴)

在這裏插入圖片描述
處理循環依賴的解決辦法:
  在 B 中創建依賴 A 時,通過 ObjectFactory 提供的實例化方法來中斷 A 中的屬性填充,使 B 中持有的 A 僅僅是剛剛初始化並沒有填充任何屬性的 A,這初始化 A 的步驟是在最開始創建 A 的時候進行的,由於 A 與 B 中的 A 所表示的屬性地址是一樣的,所以在 A 中創建好的屬性填充自然可以通過 B 中的 A 獲得。

7.3 屬性注入(填充)

處理流程

  1. postProcessAfterInstantiation 控制程序是否繼續進行填充
  2. 根據注入類型(byName、byType)提取依賴的 bean,統一存入 PropertyValues中
  3. 在屬性獲取完畢填充前,對屬性再次處理(eg:對屬性的驗證)
  4. 將所有 PropertyValues 中的屬性填充至 BeanWrapper

獲取注入屬性:

(1)autowireByName
  在傳入的參數 MutablePropertyValues 中找出已經加載的 bean,並遞歸實例化,進而加入到 MutablePropertyValues

(2)autowireByType

  1. 嘗試使用解析器進行解析
  2. 對於集合類型,不在解析範圍,需要再次對不同集合類型進行不同情況的處理

應用到已經實例化的 bean:

applyPropertyValues

  1. 獲取所有屬性
  2. 獲取對應的解析器
  3. 遍歷屬性,將屬性轉換爲對應類的對應屬性的類型

7.4 初始化 bean(進行用戶設定的初始化方法的調用)

  1. 激活 Aware 方法(實現 Aware 接口的 bean 在被初始化之後,可以取得相對應的資源<BeanFactory實例,ApplicationContext實例>)
  2. 處理器的應用(BeanPostProcessor,給用戶充足的權限更改 / 擴展Spring)
  3. 激活自定義的 init 方法

7.5 註冊 DisposableBean(銷燬方法)

銷燬方法的擴展入口
銷燬方法還可以通過:配置屬性 destroy-method 方法

發佈了165 篇原創文章 · 獲贊 97 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章