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是已實例化好的對象,但是未注入屬性和初始化,這個bean對象有可能是個AOP代理的bean。用來處理被AOP代理後的bean的循環依賴問題,這裏主要是存放的是beanName與正在創建的bean實例,這個bean有可能是已經創建好的代理的bean。如下面的代碼,從三級緩存singletonFactories取出的singletonFactory.getObject()方法其實就是調用匿名函數的getEarlyBeanReference方法,這個方法內部會調用SmartInstantiationAwareBeanPostProcessor後置處理器的getEarlyBeanReference方法裏的wrapIfNecessary方法生成代理。
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        // Quick check for existing instance without full singleton lock
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
                synchronized (this.singletonObjects) {
                    // Consistent creation of early reference within full singleton lock
                    singletonObject = this.singletonObjects.get(beanName);
                    if (singletonObject == null) {
                        singletonObject = this.earlySingletonObjects.get(beanName);
                        if (singletonObject == null) {
                            ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                            if (singletonFactory != null) {
                                singletonObject = singletonFactory.getObject();
1、----------------------------------------------------緩存到二級緩存中-----------------------------------------------
                                this.earlySingletonObjects.put(beanName, singletonObject);
                                this.singletonFactories.remove(beanName);
                            }
                        }
                    }
                }
            }
        }
        return singletonObject;
    }
2、--------------------------------------------------在調用createBeanInstance實例化bean後會保存bean到三級緩存中------------------------------------------------
addSingletonFactory(beanName, new ObjectFactory() {
                public Object getObject() throws BeansException {
                    return getEarlyBeanReference(beanName, mbd, bean);
                }
            });
3、------------------------SmartInstantiationAwareBeanPostProcessor 後置處理器是AOP會調用AbstractAutoProxyCreator類的getEarlyBeanReference生成AOP的代理----------------------------
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
        Object exposedObject = bean;
        if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
            for (BeanPostProcessor bp : getBeanPostProcessors()) {
                if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                    SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                    exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
                }
            }
        }
        return exposedObject;
    }

可以概括爲二級緩存存放的就是那些從三級緩存中取出的objectFactory對象調用getObject()方法返回的對象,這個方法會調用到getEarlyBeanReference方法給那些aop註解對象生成代理,而且這個對象是已經完成實例化但是還未完成屬性賦值或者初始化
singletonFactories:第三級緩存,存的是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,不需要容器做後續的創建工作
  • 在createBean方法中如果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的創建完成前會使用三級緩存解決循環依賴的問題,如果是AOP代理對象產生的循環依賴會使用二級緩存和三級緩存共同處理,完成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進行還原,然後返回

Spring幾個重要的拓展點

InstantiationAwareBeanPostProcessor和BeanPostProcessor、BeanDefinitionRegistryPostProcessor
@Component
public class Inintion implements InstantiationAwareBeanPostProcessor {
    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        System.out.println("InstantiationAwareBeanPostProcessor:postProcessBeforeInstantiation");
        System.out.println(beanName+"開始實例化");
        return null;
    }

    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        System.out.println("InstantiationAwareBeanPostProcessor:postProcessAfterInstantiation");
        System.out.println(beanName+"實例化完成");
        return false;
    }
}

@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println(beanName+"開始初始化了.......");
        System.out.println("BeanPostProcessor:postProcessBeforeInitialization");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
         System.out.println(beanName+"初始化完成了.......");
        System.out.println("BeanPostProcessor:postProcessAfterInitialization");
        return bean;
    }
}

@Component
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        RootBeanDefinition rootBeanDefinition = new RootBeanDefinition();
        rootBeanDefinition.setBeanClass(Hadoop.class);
        registry.registerBeanDefinition("hadoop32", rootBeanDefinition);
        System.out.println("BeanDefinitionRegistryPostProcessor:postProcessBeanDefinitionRegistry===============");
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        System.out.println("BeanDefinitionRegistryPostProcessor:postProcessBeanFactory===============");

    }
}

加載bean觀察現象


@Data
@Component
public class Html {
    private Integer id;
    private String name;

    public Html(Integer id, String name) {
        this.id = id;
        this.name = name;
    }

    public Html() {
        System.out.println("html調用了構造方法");
    }

    //@PostConstruct在容器的組件初始化後使用
    @PostConstruct
    public void post() {
        System.out.println("Html調用了@PostConstruct的方法");
    }

    //@PreDestroy在容器的組件被摧毀前被調用
    @PreDestroy
    public void destroy() {
        System.out.println("Html調用了@PreDestroy的方法");
    }
}

結論:

1、InstantiationAwareBeanPostProcessor是在bean實例化前後調用,postProcessBeforeInstantiation是在實例化前執行,postProcessAfterInstantiation是在實例化後執行
2、BeanPostProcessor是在bean初始化前後調用的,postProcessBeforeInitialization是在bean初始化前執行,postProcessAfterInitialization是在bean初始化後執行
3、BeanDefinitionRegistryPostProcessor是在bean的註冊前後調用

初始化InitializingBean

spring初始化bean有兩種方式:

  • 第一:實現InitializingBean接口,繼而實現afterPropertiesSet的方法
@Component
class MyInitializingBean implements InitializingBean {
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("InitializingBean:afterPropertiesSet===============================");
    }
}
  • 第二:反射原理,配置文件使用init-method標籤直接注入bean
<bean id="testInitializingBean" class="com.TestInitializingBean" init-method="testInit"></bean>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章