springIoC源碼解析過程,及解決循環依賴詳解

xml解析:

  • XmlBeanFactory繼承自DefaultListableBeanFactory,XmlBeanFactory使用XmlBeanDefinitionReader的loadBeanDefinitions方法將xml的Resource資源進行解析和讀取。
  • XmlBeanDefinitionReader將xml資源委託給DefaultDocumentLoader類的loadDocument方法將xml的Resource轉換爲Document資源返回給XmlBeanDefinitionReader,XmlBeanDefinitionReader調用registerBeanDefinitions方法將Document資源委託給DefaultBeanDefinitionDocumentReader,DefaultBeanDefinitionDocumentReader調用parseBeanDefinitions方法對Document資源的各個節點 進行解析並註冊。
  • 註冊主要是DefaultBeanDefinitionDocumentReader的parseBeanDefinitions方法中調用了BeanDefinitionParserDelegate類的parseBeanDefinitionElement方法對Document的節點解析成BeanDefinitionHolder,BeanDefinitionHolder中封裝了beanName、BeanDefinition、Aliases等屬性。
  • 然後調用DefaultListableBeanFactory核心類的registerBeanDefinition(beanName, definitionHolder.getBeanDefinition())方法完成BeanDefinition的向ioc容器註冊,ioc容器就是以DefaultListableBeanFactory內部的一個ConcurrentHashMap作爲存儲存儲媒介

單例bean的加載創建:

AbstractBeanFactory類getBean方法中的doGetBean這個方法蘊含了整個bean的依賴注入、bean的創建、屬性賦值、初始化、後置處理器的前後置方法的調用、循環依賴解決等。
doGetBean方法主要有兩個分支,分支一:從緩存取bean,分支二:緩存中不存在bean,從頭開始創建bean

從緩存取bean
  • doGetBean中先通過getSingleton(beanName)獲取緩存中的對應beanName的原始對象,然後調用getObjectForBeanInstance方法對應原始對象進行還原,創建出來的bean都要經過getObjectForBeanInstance方法才能完成創建,原因是創建出來的bean可能是一個factorybean工廠對象,所以需要調用此方法,內部會判斷如果是一個factorybean,就會調用其getObject方法還原bean,還原後直接返回
  • 爲解決單例模式的循環依賴,在spring創建bean的時候是不等bean創建完成就將創建的objectFactory提前曝光加入緩存中,一旦下一個bean創建需要依賴上一個bean的時候則直接使用objectFactory
    getSingleton 方法涉及了spring的三級緩存:
    singletonObjects:第一級緩存,存放可用的完全初始化,成品的Bean。存放的是beanName與bean實例
    earlySingletonObjects:第二級緩存,存放半成品的Bean,半成品的Bean是已創建對象,但是未注入屬性和初始化。用以檢測循環依賴。這裏主要是存放的是beanName與正在創建的bean實例,用來檢測循環依賴
    singletonFactories:第三級緩存,存的是Bean工廠對象,用來生成半成品的Bean並放入到二級緩存中。用以解決循環依賴。如果Bean存在AOP的話,返回的是AOP的代理對象。存放的是beanName與objectFactory,這是解決循環依賴的主要手段
從頭開始創建bean
  • 先判斷是否是多例並且存在循環依賴,是則拋出異常,不處理
  • 查找ioc容器中是否包含這個bean,不包含則需要從父類容器中getBean方法創建bean
  • ioc容器中存在此beanName的BeanDefinition,則將此BeanDefinition及其父類的屬性合併成RootBeanDefinition
  • 使用RootBeanDefinition.getDependsOn()方法查找這個bean創建需要依賴的對象,先遍歷這些屬性調用getBean創建這些依賴對象
  • 從RootBeanDefinition判斷這個bean是單例還是多例,單例則使用,以下代碼創建
//這個getSingleton和上面的getSingleton不一樣,是個重載的getSingleton方法 
sharedInstance = getSingleton(beanName, () -> {
                        try {
                            return createBean(beanName, mbd, args);
                        }
                        catch (BeansException ex) {
                            destroySingleton(beanName);
                            throw ex;
                        }
                    });
                    beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);

這裏的getSingleton主要是使用匿名函數createBean創建的objectFactory然後調用getObject完成bean的創建初始化,然後將創建好的bean實例singletonObjects一級緩存中, 然後調用getObjectForBeanInstance對原始對象進行還原

  • 這其中createBean是AbstractAutowireCapableBeanFactory的方法,在方法中會調用resolveBeforeInstantiation返回代理做前置處理,如果這個方法返回的bean不爲空,則說明bean對象是代理使用前置處理生成的beanbean,不需要容器做後續的創建工作
  • 如果resolveBeforeInstantiation方法返回的是空對象,那麼就需要使用doCreateBean進行創建了,doCreateBean方法是蘊含了bean對象的實例化、屬性賦值、實例化的一系列生命週期的方法,其中夾雜了許多前後置的處理調用,這個方法非常關鍵
單獨分析doCreateBean方法

doCreateBean也是AbstractAutowireCapableBeanFactory的方法,是bean進行常規創建的方法。

  • bean的實例化。先使用createBeanInstance方法進行bean的實例化,這是bean生命週期的第一個階段,裏面包含裏多種實例化策略,包括了工廠方法、構造函數自動注入、簡單初始化等,具體的邏輯是,如果BeanDefinition中包含工廠方法優先使用工廠方法實例化、否則spring會更加參數去鎖定使用bean中的哪一個構造方法,如果不是工廠方法也不存在帶參數的構造方法,則使用默認是無參構造進行實例化,實例化的bean是以BeanWrapper類型存在,BeanWrapper封裝了bean各類消息,通過BeanWrapper.getWrappedInstance返回實例化的bean。
  • 調用完createBeanInstance實例化完bean後,然後spring爲了解決循環依賴,會在完成bean創建前調用addSingletonFactory方法,將objectFactory放入三級緩存中,以此解決循環依賴。objectFactory是通過實例化的bean實例去生成的。
  • 這裏我們單獨說一下spring如何解決循環依賴,spring調用了我們剛剛提到的addSingletonFactory方法,代碼如下:
//mbd是RootBeanDefinition,bean是BeanWrapper調用getWrappedInstance()返回的剛剛實例化的bean
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

我們在看看裏面的匿名函數:

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
        Object exposedObject = bean;
        if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
            for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
                exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
            }
        }
        return exposedObject;
    }

這裏使用了SmartInstantiationAwareBeanPostProcessor的後置處理器的調用,對bean進行處理,aop將advice動態織入也是在這裏實現的,也就是基於SmartInstantiationAwareBeanPostProcessor類實現的動態織入,spring如何使用其解決循環依賴依賴的呢?假如此時A創建時需要創建B,B創建時需要A,而此時通過提供objectFactory來填充B的依賴屬性A,因爲A是正在創建進行中,只進行了實例化,沒有進行任何的屬性賦值,但是objectFactory就是在此方法中通過剛剛實例化的A的實例bean生成的,所以此objectFactory指向的屬性地址是與B中持有的A是一樣的,B也只是僅僅需要A類型的屬性賦值以便完成創建而已,所以A能填充到B中,這樣就解決了循環依賴。這僅僅只是getset方法注入及單例才能解決的循環依賴。如果是構造器注入的話是沒有辦法解決循環依賴的,原因是A構造器注入時需要一個創建好的B,B構造器注入時需要一個創建好的A,因此A和B沒辦法實例化,提供一個A或者B屬性的對象,這樣就沒有辦法解決循環依賴。但是getset注入就可以使用無參構造一個bean,所以能做到完成屬性賦值等一系列操作前暴露一個使用構造好的bean創建的objectFactory去解決循環依賴,並不需要完成創建好。多例模式無法解決循環依賴的是因爲bean每次使用時都需要創建一個最新的對象,對象沒辦法進行復用所以容器不會進行緩存,就無法提前暴露一個bean。
當面試官提問spring如何解決循環依賴時可以這麼會答:在單例模式getset注入的情況下,spring的創建bean的過程中,bean進行實例化後會暴露一個objectFactory並存入三級緩存中,在還沒有完成bean的創建完成前會使用三級緩存解決循環依賴的問題,完成bean的創建後就會將對應的三級緩存移除,並存入一級緩存,以便下次使用時可以複用

  • bean的屬性賦值,回到doCreateBean方法調用完addSingletonFactory方法之後會使用populateBean方法對bean進行屬性賦值,這是bean生命週期的第二個階段,詳細過程是從RootBeanDefinition獲取PropertyValues對象,根據條件決定調用autowireByName方法按名稱調用還是autowireByType按類型注入,無論是按名稱注入還是按類型注入都是根據RootBeanDefinition和BeanWrapper去查找需要注入的依賴bean,然後對這些bean進行循環遍歷並調用getBean方法一一創建依賴對象,將依賴對象存儲到PropertyValues中,變量後置處理器對PropertyValues中的依賴屬性進行後置處理,然後調用applyPropertyValues方法將PropertyValues設置的依賴對象應用到已經實例化好的bean到中,這樣就完成了bean的屬性賦值
  • bean的初始化,回到doCreateBean方法繼續調用initializeBean去完成bean生命週期的第三個階段:初始化此時bean已經完成了實例化以及屬性賦值,這時就會調用用戶設定的初始化方法。方法中調用invokeAwareMethods這個方法會調用一系列的xxxxAware類的方法,applyBeanPostProcessorsBeforeInitialization是遍歷postPostProcessor的前置方法並調用,applyBeanPostProcessorsAfterInitialization是遍歷postPostProcessor的後置方法並調用。
  • 初始化完成後返回實例對象,然後返回到doGetBean方法中,將其傳入getObjectForBeanInstance方法將bean進行還原,然後返回

這就是單例bean創建的大概過程

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