深入spring循環(huán)依賴處理機(jī)制與源碼解析

文章內(nèi)容來源:拉勾教育Java高薪訓(xùn)練營(yíng)8期

知識(shí)的獲取,來源于輸出!這是我在拉勾教育Java高薪訓(xùn)練營(yíng)8期學(xué)習(xí)后的一個(gè)總結(jié)與分享,選取spring的循環(huán)依賴處理這一知識(shí)點(diǎn)做總結(jié),因?yàn)槲矣X得它涵蓋spring創(chuàng)建bean的流程,既梳理了循環(huán)依賴,也能對(duì)bean的創(chuàng)建流程進(jìn)行一個(gè)回顧。

1.談?wù)勗诶唇逃?xùn)練營(yíng)的學(xué)習(xí)感受

1.1 報(bào)名學(xué)習(xí)初衷

技術(shù)遇到瓶頸,目前的工作技術(shù)對(duì)技術(shù)提升難度大,給自己一個(gè)提升于與學(xué)習(xí)的機(jī)會(huì)

1.2 學(xué)習(xí)方式

直播+錄播,有班主任負(fù)責(zé)學(xué)習(xí)事務(wù),導(dǎo)師負(fù)責(zé)指導(dǎo)學(xué)習(xí)

1.3 學(xué)習(xí)感受

老師講課能夠由淺入深,有深度也有廣度,跟著老師學(xué)下來。對(duì)相對(duì)復(fù)雜的知識(shí)也能掌握(如框架源碼),最終能夠手寫實(shí)現(xiàn)核心邏輯

1.4 老師

班主任認(rèn)真負(fù)責(zé),經(jīng)常我們監(jiān)督學(xué)習(xí)和服務(wù)同學(xué)們,導(dǎo)師也是有問必答,幫助同學(xué)學(xué)習(xí)、解惑

2.spring循環(huán)依賴處理機(jī)制

2.1 什么是循環(huán)依賴

循環(huán)依賴就是指對(duì)象之間的相互引用,通俗來講,就是兩個(gè)或者兩個(gè)以上的對(duì)象互相持有對(duì)方的引用,用代碼表示就是:

public class LagouBean {
    private ItBean itBean;
    ...
}
public class ItBean {
    private LagouBean lagouBean;
    ...
}

在開發(fā)中使用spring時(shí),對(duì)象一般都是交給spring來創(chuàng)建和管理,就像下面這樣:

<bean id="lagouBean" class="com.lagou.edu.LagouBean">
    <property name="ItBean" ref="itBean"/>
</bean>
<bean id="itBean" class="com.lagou.edu.ItBean">
    <property name="LagouBean" ref="lagouBean"/>
</bean>

如上兩個(gè)類,在創(chuàng)建LagouBean的時(shí)候發(fā)現(xiàn)它依賴了ItBean,于是要先去創(chuàng)建ItBean,這時(shí)候發(fā)現(xiàn)ItBean又依賴了LagouBean,此時(shí)又要去創(chuàng)建LagouBean,好像陷入了一種死循環(huán)...但在使用spring的時(shí)候,發(fā)現(xiàn)它并不會(huì)進(jìn)入死循環(huán)導(dǎo)致程序異常,反而能夠正常創(chuàng)建對(duì)象供程序調(diào)用,那么它是如何處理的?

2.2 spring如何處理循環(huán)依賴

經(jīng)過翻閱spring創(chuàng)建bean部分的源碼得知,spring處理循環(huán)依賴的核心思想是基于java的引用傳遞,獲得對(duì)象的引用時(shí),屬性是可以延后設(shè)置的。利用這一特性,spring在創(chuàng)建對(duì)象時(shí),先不設(shè)置其屬性,實(shí)例化后提前暴露到容器中,提供給其他對(duì)象引用。

所以創(chuàng)建LagouBean時(shí),實(shí)例化后立即暴露到容器中,然后填充屬性,此時(shí)發(fā)現(xiàn)它依賴了ItBean,則先去創(chuàng)建ItBean,ItBean實(shí)例化后也會(huì)提前暴露,后填充屬性,此時(shí)發(fā)現(xiàn)ItBean依賴了LagouBean,因?yàn)長(zhǎng)agouBean提前已經(jīng)暴露出來了,就可以從容器中取到完成ItBean的創(chuàng)建,ItBean創(chuàng)建完成,填充到LagouBean完成創(chuàng)建。

spring為了完成這一過程,采用了三級(jí)緩存機(jī)制,對(duì)象的提前暴露就是將創(chuàng)建的對(duì)象事先放入緩存之中。在org.springframework.beans.factory.support.DefaultSingletonBeanRegistry類中,定義了這三個(gè)緩存:

Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
Map<String, Object> earlySingletonObjects = new HashMap<>(16);
  • singletonObjects:一級(jí)緩存,存放創(chuàng)建完成的單例對(duì)象,是完完整整的對(duì)象,經(jīng)歷了實(shí)例化、屬性填充、后置處理等一系列步驟后創(chuàng)建的對(duì)象
  • singletonFactories:三級(jí)緩存,存放的是將實(shí)例化后的bean包裝成的ObjectFactory
  • earlySingletonObjects:二級(jí)緩存,存放的是提前暴露的bean,未填充屬性,但是已經(jīng)可以被其他對(duì)象引用了,這里bean的獲取主要是調(diào)用三級(jí)緩存中存放的ObjectFactory的getObject()方法創(chuàng)建

綜上,spring處理循環(huán)依賴的思想主要基于java引用傳遞,采用三級(jí)緩存機(jī)制,將對(duì)象僅實(shí)例化后提前暴露存放至緩存中,使得依賴它的對(duì)象可以獲取其引用完成創(chuàng)建,然后進(jìn)行屬性填充、初始化等操作,最后完成對(duì)象的創(chuàng)建。附上一張學(xué)習(xí)時(shí)的圖方便理解:

解決循環(huán)依賴問題圖解

3.spring循環(huán)依賴處理源碼解析

經(jīng)過上一部分的大致分析,這部分將對(duì)源碼進(jìn)行一個(gè)解讀,深入了解一下spring處理循環(huán)依賴的整個(gè)流程,寫了如下一段測(cè)試代碼:

public void testIoC() {
  ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
  LagouBean lagouBean = applicationContext.getBean(LagouBean.class);
  System.out.println(lagouBean);
}

3.1 容器初始化源碼解析

如上面的測(cè)試代碼,使用了ClassPathXmlApplicationContext容器作為測(cè)試對(duì)象,通過代碼調(diào)試跟蹤,最開始調(diào)用了ClassPathXmlApplicationContext的構(gòu)造方法,方法內(nèi)部調(diào)用了org.springframework.context.support.AbstractApplicationContext#refresh()來完成容器的創(chuàng)建

public ClassPathXmlApplicationContext(
            String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)throws BeansException {
        //初始化父類
        super(parent);
        //設(shè)置配置信息
        setConfigLocations(configLocations);
        //完成容器的初始化
        if (refresh) {
            refresh();
        }
    }

refresh()方法的核心就是完成BeanFactory的創(chuàng)建,加載beandefition,然后進(jìn)行bean的創(chuàng)建

public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            //創(chuàng)建beanfactory,加載beandefition注冊(cè)到beandefiitoRegisty
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
            //初始化bean,填充屬性,初始化方法調(diào)用..
            finishBeanFactoryInitialization(beanFactory);
        }
    }

finishBeanFactoryInitialization()方法中,調(diào)用了org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons()方法

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
        //實(shí)例化所有非延遲加載的bean
        beanFactory.preInstantiateSingletons();
    }

preInstantiateSingletons()中調(diào)用了org.springframework.beans.factory.support.AbstractBeanFactory#getBean(String name)方法,Bean的創(chuàng)建流程就是從這里開始的,getBean()自調(diào)用了doGetBean()方法,開始真正創(chuàng)建bean了

3.2 獲取對(duì)象doGetBean方法解析

protected <T> T doGetBean() throws BeansException {
        Object bean;
        //標(biāo)記:1
        Object sharedInstance = getSingleton(beanName);
        if (sharedInstance != null && args == null) {
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
        } else {
            try {
                if (mbd.isSingleton()) {
                     //標(biāo)記:2
                    sharedInstance = getSingleton(beanName, () -> {
                        // 創(chuàng)建單例Bean的主要方法,返回的bean是完整的
                        return createBean(beanName, mbd, args);
                    });
                    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
                }
            }

        }
        return (T) bean;
    }

doGetBean()方法的主要邏輯為:

  • 根據(jù)beanName嘗試從緩存中取對(duì)象LagouBean
  • 取到就返回對(duì)應(yīng)的實(shí)例
  • 未取到,則進(jìn)行bean的創(chuàng)建

其中重點(diǎn)標(biāo)記處:

  • 標(biāo)記1: 調(diào)用org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(String beanName)方法,嘗試從緩存中取對(duì)象
  • 標(biāo)記2: 判斷如果要?jiǎng)?chuàng)建的對(duì)象是單例對(duì)象,調(diào)用getSingleton的重載方法getSingleton(String beanName, ObjectFactory<?> singletonFactory)進(jìn)行對(duì)象的創(chuàng)建,它的內(nèi)部,有兩段重點(diǎn)代碼:
//標(biāo)記:2-1
singletonObject = singletonFactory.getObject();
//標(biāo)記:2-2
addSingleton(beanName, singletonObject);
  • 標(biāo)記2-1: 從上面的標(biāo)記2中可以看出singletonFactory.getObject()調(diào)用的是方法createBean(beanName, mbd, args)返回最終創(chuàng)建的對(duì)象
  • 標(biāo)記2-2: 新創(chuàng)建的bean添加到【一級(jí)緩存】中,以后getBean的時(shí)候就可以直接獲取了,源碼如下:
protected void addSingleton(String beanName, Object singletonObject) {
        synchronized (this.singletonObjects) {
            // 將新創(chuàng)建的bean添加到一級(jí)緩存中
            this.singletonObjects.put(beanName, singletonObject);
            // 從其他緩存中移除相關(guān)的bean
            this.singletonFactories.remove(beanName);
            this.earlySingletonObjects.remove(beanName);
  }
}

3.3 創(chuàng)建對(duì)象doCreateBean源碼解析

上面說到創(chuàng)建對(duì)象的方法在createBean(beanName, mbd, args),內(nèi)部調(diào)用了org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean方法,其內(nèi)部邏輯分為5大步,這里將其拆開來剖析:

3.3.1 實(shí)例化對(duì)象
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
            throws BeanCreationException {
          BeanWrapper instanceWrapper = null;
        
        // 調(diào)用無參構(gòu)造實(shí)例化Bean
        if (instanceWrapper == null) {
            instanceWrapper = createBeanInstance(beanName, mbd, args);
        }
        // 實(shí)例化后的Bean對(duì)象,這里獲取到的是一個(gè)原始對(duì)象,即沒有進(jìn)行屬性填充的對(duì)象
        final Object bean = instanceWrapper.getWrappedInstance();
    }

這部分的主要邏輯就是調(diào)用無參構(gòu)造實(shí)例化,拿到一個(gè)原始的Bean

3.3.2 將原始對(duì)象放入三級(jí)緩存
protected Object doCreateBean() {
         boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
                isSingletonCurrentlyInCreation(beanName));
        if (earlySingletonExposure) {
            addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
        }
    }
  • earlySingletonExposure:是否”提前暴露“原始對(duì)象的引用,如果需要提前暴露單例bean,則將該bean工廠放入【三級(jí)緩存】中
  • 方法addSingletonFactory則是將原始對(duì)象包裝成ObjectFactory放入三級(jí)緩存,源碼如下:
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
        synchronized (this.singletonObjects) {
            if (!this.singletonObjects.containsKey(beanName)) {
                //向三級(jí)緩存里添加ObjectFactory
                this.singletonFactories.put(beanName, singletonFactory);
               }
        }
    }
  • 匿名內(nèi)部方法 getEarlyBeanReference是SmartInstantiationAwareBeanPostProcessor接口的的一個(gè)默認(rèn)方法,實(shí)現(xiàn)這個(gè)方法的只有org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator這個(gè)類,實(shí)現(xiàn)邏輯為:
public Object getEarlyBeanReference(Object bean, String beanName) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        this.earlyProxyReferences.put(cacheKey, bean);
        return wrapIfNecessary(bean, beanName, cacheKey);
    }

它內(nèi)部自調(diào)用了方法 wrapIfNecessary:

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        // Create proxy if we have advice.
        if (specificInterceptors != DO_NOT_PROXY) {
            Object proxy = createProxy(
                    bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
            return proxy;
        }
        return bean;
    }

這個(gè)方法關(guān)鍵點(diǎn)在 createProxy,顧名思義它是為bean創(chuàng)建代理對(duì)象的,所以它的整個(gè)邏輯就是,如果當(dāng)前bean需要?jiǎng)?chuàng)建代理對(duì)象,那么它返回的就是代理對(duì)象,否則返回原始對(duì)象。

那么getEarlyBeanReference在什么時(shí)候調(diào)用的?

還記得在3.2章節(jié)講述doGetBean方法時(shí)提到首先會(huì)從緩存中嘗試獲取bean嗎?如下:

protected <T> T doGetBean() throws BeansException {
    Object sharedInstance = getSingleton(beanName);
}

調(diào)用的getSingleton方法就是嘗試從緩存中獲取bean,這里來看一下它的源碼:

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        // 從 singletonObjects 獲取實(shí)例,singletonObjects 中的實(shí)例都是準(zhǔn)備好的 bean 實(shí)例,可以直接使用
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            synchronized (this.singletonObjects) {
                // 從二級(jí)緩存中獲取單例bean
                singletonObject = this.earlySingletonObjects.get(beanName);
                // allowEarlyReference :表示是否允許從singletonFactories中通過getObject拿到對(duì)象
                if (singletonObject == null && allowEarlyReference) {
                    // 從三級(jí)緩存中獲取單例bean,并移動(dòng)到二級(jí)緩存
                    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
                        // 通過單例工廠獲取單例bean
                        singletonObject = singletonFactory.getObject();
                        // 從三級(jí)緩存移動(dòng)到了二級(jí)緩存,并移除singletonFactory
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }
        return singletonObject;
    }

可以看到,從三級(jí)緩存獲取對(duì)象時(shí),調(diào)用了singletonFactory.getObject(),這里最終調(diào)用的就是匿名內(nèi)部方法getEarlyBeanReference。

所以可以說三級(jí)緩存的功效就是,在bean放入【二級(jí)代理】之前,通過ObjectFactory為當(dāng)前bean創(chuàng)建代理對(duì)象(如果需要的話),保證別的bean引用它時(shí)是一個(gè)代理對(duì)象。

3.3.3 屬性填充

經(jīng)過上一小節(jié)的分析可知:

  • 原始對(duì)象已經(jīng)創(chuàng)建成功
  • 原始對(duì)象包裝成ObjectFactory放入了三級(jí)緩存

接下來,就是對(duì)bean進(jìn)行屬性填充了

protected Object doCreateBean() {
       ...
      populateBean(beanName, mbd, instanceWrapper);
      ...
}

屬性填充調(diào)用了populateBean方法,它最終調(diào)用了 applyPropertyValues進(jìn)行屬性填充:

protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
        
        for (PropertyValue pv : original) {
            if (pv.isConverted()) {
                deepCopy.add(pv);
            }
            else {
                //獲取依賴的bean
                String propertyName = pv.getName();
                Object originalValue = pv.getValue();
                //獲取依賴的對(duì)象
                Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
        }
    }

方法applyPropertyValues內(nèi)部遍歷處理依賴,獲取依賴的bean,調(diào)用org.springframework.beans.factory.support.BeanDefinitionValueResolver#resolveValueIfNecessary方法去獲取被依賴的對(duì)象:

public Object resolveValueIfNecessary(Object argName, @Nullable Object value) {
        if (value instanceof RuntimeBeanReference) {
            RuntimeBeanReference ref = (RuntimeBeanReference) value;
            return resolveReference(argName, ref);
        }
    }

方法內(nèi)部又自調(diào)用了resolveValueIfNecessary方法:

private Object resolveReference(Object argName, RuntimeBeanReference ref) {
        //獲取或創(chuàng)建依賴對(duì)象
        bean = this.beanFactory.getBean(refName);
        return bean;
    }

方法 resolveReference根據(jù)獲取的beanName調(diào)用beanFactory的getBean方法去獲取依賴的bean,此時(shí)如果bean還未被創(chuàng)建,則會(huì)走一遍創(chuàng)建bean的流程,之后拿到依賴的bean,進(jìn)行屬性填充。

以文章開頭的LagouBean與ItBean為例,說明一下整個(gè)流程:
(1)首先嘗試從緩存中獲取LagouBean,發(fā)現(xiàn)沒有,則創(chuàng)建LagouBean,實(shí)例化后,放入三級(jí)緩存
(2)為L(zhǎng)agouBean填充屬性,發(fā)現(xiàn)依賴了ItBean
(3)創(chuàng)建ItBean,實(shí)例化后,放入三級(jí)緩存
(4)為ItBean填充屬性,發(fā)現(xiàn)依賴了LagouBean,于是嘗試從緩存中獲取LagouBean
(5)發(fā)現(xiàn)LagouBean在三級(jí)緩存中,則調(diào)用對(duì)應(yīng)的objectFactory.getObject()拿到對(duì)象LagouBean(如果LagouBean需要?jiǎng)?chuàng)建代理,也是在這一步完成的)
(6)拿到對(duì)象LagouBean后移動(dòng)到二級(jí)緩存,可供其他對(duì)象引用了,ItBean也因此可以完成屬性填充,初始化,完成創(chuàng)建,最后放入一級(jí)緩存,返回給LagouBean
(7) LagouBean拿到ItBean,繼續(xù)走填充流程,直到填充完畢,創(chuàng)建完成,放入一級(jí)緩存,到此LagouBean就可以使用了,解決了循環(huán)依賴問題

通過上述流程發(fā)現(xiàn),如果LagouBean 與ItBean都需要?jiǎng)?chuàng)建代理對(duì)象,LagouBean的代理對(duì)象的創(chuàng)建在第(5)可完成,因?yàn)镮tBean的創(chuàng)建在LagouBean的屬性填充過程中,創(chuàng)建完直接放入了一級(jí)緩存,沒有走二級(jí)緩存,ItBean的代理對(duì)象好像并沒有創(chuàng)建的地方,那么spring如何保證LagouBean最終依賴的是ItBean的代理對(duì)象呢?其實(shí)ItBean的代理對(duì)象發(fā)生在bean初始化這一步,它在屬性填充之后執(zhí)行。

3.3.4 初始化

初始化就是給創(chuàng)建好的bean做后置處理,就像創(chuàng)建了一個(gè)人,populateBean就是給人填充各種五臟六腑,血肉...初始化就是給人穿上外衣,如下圖源碼所示,initializeBean方法就是對(duì)bean進(jìn)行初始化

protected Object doCreateBean() {
        //填充屬性(依賴注入)
        populateBean(beanName, mbd, instanceWrapper);
        //調(diào)用初始化方法,完成bean的初始化操作
        exposedObject = initializeBean(beanName, exposedObject, mbd);
        return exposedObject;
    }

initializeBean方法做了4個(gè)動(dòng)作,如下

protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
        invokeAwareMethods(beanName, bean);
        Object wrappedBean = bean;
        //before
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
        //觸發(fā)初始化方法的調(diào)用
        invokeInitMethods(beanName, wrappedBean, mbd);
        //after
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
        return wrappedBean;
    }
  • invokeAwareMethods方法主要給bean設(shè)置beanName,beanfactory,applicationContext
  • applyBeanPostProcessorsBeforeInitialization指預(yù)初始化方法,內(nèi)部比較簡(jiǎn)單
  • invokeInitMethods方法做了兩件事:一個(gè)是調(diào)?InitializingBean
    的afterPropertiesSet?法,一個(gè)是調(diào)用自定義的init方法
protected void invokeInitMethods() {
        //調(diào)用InitializingBean的afterPropertiesSet()
        ((InitializingBean) bean).afterPropertiesSet();
        //調(diào)用自定義的init方法
        invokeCustomInitMethod(beanName, bean, mbd);
    }
  • applyBeanPostProcessorsAfterInitialization后初始化方法,它內(nèi)部調(diào)用了接口org.springframework.beans.factory.config. BeanPostProcessorpostProcessAfterInitialization方法,這個(gè)方法的實(shí)現(xiàn)類為 org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator,它的源碼如下,有兩個(gè)重要的方法:
public abstract class AbstractAutoProxyCreator{

    public Object getEarlyBeanReference(Object bean, String beanName) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        //放進(jìn)緩存earlyProxyReferences里,bean此時(shí)是原始對(duì)象,標(biāo)記該對(duì)象是否被循環(huán)依賴
        this.earlyProxyReferences.put(cacheKey, bean);
        //創(chuàng)建代理對(duì)象
        return wrapIfNecessary(bean, beanName, cacheKey);
    }

    public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
        if (bean != null) {
            Object cacheKey = getCacheKey(bean.getClass(), beanName);
           // remove方法返回記錄的bean
        // 若被循環(huán)引用了,說明執(zhí)行了getEarlyBeanReference方法,remove返回值是==bean的
// 若沒被循環(huán)引用,getEarlyBeanReference沒執(zhí)行 remove方法返回null,所以就進(jìn)入創(chuàng)建代理對(duì)象步驟
            if (this.earlyProxyReferences.remove(cacheKey) != bean) {
                return wrapIfNecessary(bean, beanName, cacheKey);
            }
        }
        return bean;
    }
}

在3.3.2章節(jié)中講過,getEarlyBeanReference是處理循環(huán)依賴的關(guān)鍵方法,它的執(zhí)行要先于postProcessAfterInitialization,功能就是創(chuàng)建好代理對(duì)象,最終放入二級(jí)緩存,提前暴露代理對(duì)象的引用。

postProcessAfterInitialization方法執(zhí)行在處理循環(huán)依賴之后,所以先進(jìn)行判斷有沒有被循環(huán)引用過,沒有循環(huán)引用過,則會(huì)創(chuàng)建它的代理對(duì)象返回。

所以,前文中所說的ItBean沒有被循環(huán)引用,如果它需要?jiǎng)?chuàng)建代理對(duì)象的話,在postProcessAfterInitialization這里就會(huì)去創(chuàng)建它的代理對(duì)象。最終保證依賴ItBean的類也能拿到它的代理對(duì)象引用。

到此,循環(huán)依賴處理也就講述結(jié)束了。最后附上一張我自己手繪的圖方便理解:

循環(huán)依賴處理流程

4.spring循環(huán)依賴常見問題總結(jié)

1.作用域?yàn)閜rototype的 bean對(duì)象支持循環(huán)依賴處理嗎?

不支持。prototype的 bean存在循環(huán)引用的話會(huì)直接跑出異常org.springframework.beans.factory.BeanCurrentlyInCreationException,spring解決循環(huán)依賴的核心思想就是基于java的引用對(duì)bean緩存,提前曝光給其他對(duì)象引用,對(duì)于prototype類型的每次請(qǐng)求都會(huì)創(chuàng)建一個(gè)bean,循環(huán)引用太多了的話,無法進(jìn)行緩存處理,也就無法提前曝光引用。

2.如果構(gòu)造器注入方式也有循環(huán)依賴情況,支持嗎?

不支持。spring處理循環(huán)依賴的核心思想是基于java的引用傳遞,提前實(shí)例化好bean進(jìn)行曝光。實(shí)例化bean需要調(diào)用構(gòu)造器,存在循環(huán)依賴的情況時(shí),會(huì)無法實(shí)例化提前曝光,所以構(gòu)造器的循環(huán)依賴無法解決。

3.二級(jí)緩存能解決循環(huán)依賴嗎?

  • 沒有AOP的情況下,二級(jí)緩存能解決循環(huán)依賴
  • 有AOP的情況下,就無法解決了,因?yàn)樽⑷氲狡渌鸼ean里的要是一個(gè)代理對(duì)象,如果只有二級(jí)緩存,那么注入到其他bean里的就有可能是個(gè)原始對(duì)象。這里有人說Bean 在實(shí)例化后就完成 代理對(duì)象的創(chuàng)建不就解決問題了嗎?這樣的話,其實(shí)連二級(jí)緩存都不需要了,一個(gè)緩存就夠了,里面又處理原始對(duì)象,又處理代理對(duì)象,又要完成初始化等,這么想想就不是很優(yōu)雅,也違背了單一原則,使用三級(jí)緩存各司其職,也體現(xiàn)了spring在設(shè)計(jì)上的優(yōu)雅。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容