欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Spring大白話(huà)之三級(jí)緩存如何解決循環(huán)依賴(lài)問(wèn)題

 更新時(shí)間:2025年02月19日 11:11:28   作者:拽著尾巴的魚(yú)兒  
Spring通過(guò)三級(jí)緩存(singletonObjects、earlySingletonObjects、singletonFactories)解決單例循環(huán)依賴(lài),三級(jí)緩存使用Lambda表達(dá)式提前暴露bean的早期引用,確保在遞歸調(diào)用時(shí)能夠正確獲取對(duì)象實(shí)例,避免死循環(huán)

前言

在使用Spring 開(kāi)發(fā)過(guò)程中,我們需要對(duì)定義后的bean 通過(guò)構(gòu)造方法,或者bean 注入的方式注入到某個(gè)類(lèi)中進(jìn)而使用改bean 對(duì)應(yīng)的方法,在此過(guò)程中就會(huì)出現(xiàn)一個(gè)類(lèi)中注入了n多個(gè)bean,幾個(gè)類(lèi)中bean 相互注入,出現(xiàn) A 依賴(lài)B,B依賴(lài)C,C又依賴(lài)A/B 這種循環(huán)情況的出現(xiàn)。

提示:以下是本篇文章正文內(nèi)容,下面案例可供參考

一、Spring 循環(huán)依賴(lài)是什么?

循環(huán)依賴(lài)是指在Spring容器中,存在兩個(gè)或多個(gè)Bean之間的互相依賴(lài)關(guān)系,形成了一個(gè)閉環(huán)的依賴(lài)鏈。具體來(lái)說(shuō),當(dāng)Bean A依賴(lài)Bean B,同時(shí)Bean B又依賴(lài)Bean A時(shí),就產(chǎn)生了循環(huán)依賴(lài)。循環(huán)依賴(lài)可能會(huì)導(dǎo)致以下問(wèn)題:

死鎖:如果循環(huán)依賴(lài)的解析過(guò)程不正確,可能會(huì)導(dǎo)致死鎖。當(dāng)容器無(wú)法確定如何先實(shí)例化哪個(gè)Bean時(shí),可能會(huì)造成死鎖情況,導(dǎo)致應(yīng)用程序無(wú)法繼續(xù)正常執(zhí)行。

未完全初始化的Bean:在解決循環(huán)依賴(lài)時(shí),Spring使用了代理對(duì)象來(lái)解決依賴(lài)問(wèn)題。這意味著當(dāng)Bean A注入到Bean B中時(shí),A可能是一個(gè)未完全初始化的代理對(duì)象,而不是完全實(shí)例化,屬性注入,初始化的對(duì)象。這可能導(dǎo)致在早期階段的Bean存在一些限制和潛在的問(wèn)題。

為了解決循環(huán)依賴(lài)的問(wèn)題,Spring使用了一個(gè)兩階段的解析過(guò)程:實(shí)例化階段和注入階段。在實(shí)例化階段,Spring創(chuàng)建對(duì)象并將其放入緩存中;在注入階段,Spring解決依賴(lài)關(guān)系并完成注入。

SpringBoot 從 2.6 之前默認(rèn)開(kāi)啟循環(huán)依賴(lài),之后 開(kāi)始默認(rèn)不允許出現(xiàn) Bean 循環(huán)引用,如果需要?jiǎng)t進(jìn)行手動(dòng)開(kāi)啟:

spring:
  main:
    allow-circular-references:true

二、Spring 三級(jí)緩存解決單例的循環(huán)依賴(lài)

Spring 三級(jí)緩存 實(shí)際上使用了3個(gè)Map 來(lái)打破單例對(duì)象循環(huán)依賴(lài)的問(wèn)題,singletonObjects:這是一級(jí)緩存,保存已經(jīng)實(shí)例化且完成了所有的依賴(lài)注入和初始化的單例bean實(shí)例。earlySingletonObjects:這是二級(jí)緩存,保存已經(jīng)實(shí)例化但尚未完成所有的依賴(lài)注入和初始化的單例bean實(shí)例。

它主要用于解決屬性注入時(shí)的循環(huán)依賴(lài)問(wèn)題。singletonFactories:這是三級(jí)緩存,保存創(chuàng)建單例bean實(shí)例的ObjectFactory。

2.1 Bean 單例對(duì)象生成的過(guò)程

對(duì)象的生成必須先通過(guò)其構(gòu)造方法進(jìn)行實(shí)例化,然后對(duì)其屬性賦值,然后執(zhí)行初始化;以下以 Aservice ,Bservice,Cservice 為例進(jìn)行研究;

public interface Aservice {
}
public interface Bservice {
}
public interface Cservice {
}
@Service
public class AserviceImpl implements Aservice {
    @Autowired
    private Bservice bservice;

}
@Service
public class BserviceImpl implements Bservice {

    @Autowired
    private Aservice aservice;

}
@Service
public class CserviceImpl implements Cservice {
    @Autowired
    private Aservice aservice;
    @Autowired
    private Bservice bservice;
}

其中Aservice 依賴(lài)Bservice 的bean ,Bservice 依賴(lài)Aservice 的bean,Cservice 依賴(lài)Aservice ,Bservice 的bean ;

思考循環(huán)依賴(lài)的注入過(guò)程:

  • 首先 A 實(shí)例化,通過(guò)A 類(lèi)的構(gòu)造方法進(jìn)行 實(shí)例的構(gòu)建并返回; 對(duì)其中的bservice 屬性值進(jìn)行依賴(lài)注入;
  • 此時(shí)需要先從單例池中去獲取,Bservice 的bean ,Bservice 的bean 還沒(méi)有被創(chuàng)建,所以此時(shí)需要先創(chuàng)建Bservice 的bean ;
  • 調(diào)用Bservice 的構(gòu)造方法進(jìn)行實(shí)例的創(chuàng)建,然后對(duì)Bservice 進(jìn)行屬性注入時(shí)需要 對(duì)Aservice 進(jìn)行 屬性值設(shè)置;

然后在從單例池中獲取Aservice 的bean,發(fā)現(xiàn)沒(méi)有Aservice 的bean ,這個(gè)時(shí)候如果重復(fù)在走Aservice 的bean 創(chuàng)建過(guò)程就會(huì)陷入死循環(huán),顯然我們的項(xiàng)目此時(shí)是可以成功啟動(dòng)的,也即沒(méi)有陷入死循環(huán)中,那么Spring 是怎么解決的?

如果說(shuō)在創(chuàng)建Aservice 的bean時(shí)是分為兩步:

  • 步驟1: 先通過(guò)其構(gòu)造方法完成實(shí)例化;
  • 步驟2: 對(duì)其屬性進(jìn)行填充;

那么如果我們?cè)诓襟E1 之后 ,就將還沒(méi)有完成初始化的Aservice 的bean 放入到某個(gè)地方,然后在初始化其他bean 的時(shí)候 如果發(fā)現(xiàn)依賴(lài)了Aservice 的bean 此時(shí)可以直接注入Aservice 的bean完成對(duì)其的引用,即使Aservice 的bean還沒(méi)有進(jìn)行完整的初始化,我們對(duì)Aservice 的bean 進(jìn)行了提前暴露;

這樣在Aservice 的bean真正完成初始化之后,對(duì)Aservice 的bean引用也隨即完成;這樣就打破了bean 的循環(huán)依賴(lài),bean 可以正常初始化了;

現(xiàn)在Bservice,Cservice 中都依賴(lài)了Aservice 的bean ,顯然無(wú)法對(duì)Aservice 的單例bean 實(shí)例化兩次 ,那么就需要有個(gè)地方來(lái)存放Aservice 的這個(gè)還沒(méi)有完全初始化的bean,這樣后續(xù)其它的bean 在注入Aservice 的bean 時(shí) 會(huì)發(fā)現(xiàn) Aservice 的bean 已經(jīng)有了,所以就可以直接使用,不需要在額外創(chuàng)建,這里spring 使用 map (二級(jí)緩存)來(lái)存放已經(jīng)實(shí)例化,但是還沒(méi)有完全初始化的 bean , 以便于在發(fā)生循環(huán)依賴(lài)時(shí),如果從單例池中獲取不到對(duì)應(yīng)的bean 就到二級(jí)緩存中在獲取一次,如果獲取到了可以直接使用,如果獲取不到則需要去生成這個(gè)bean 并將其放入到二級(jí)緩存中;

因?yàn)锽service 中已經(jīng)將Aservice的bean 放入到了二級(jí)緩存中,所以Cservice 可以直接從二級(jí)緩存中獲取到service 的單例bean ;

到此看起來(lái)spring 已經(jīng)通過(guò)二級(jí)緩存來(lái)提前暴露未初始化完成的bean 而解決了循環(huán)依賴(lài),那么為什么還有三級(jí)緩存的概念?

在原碼中可以看到三級(jí)緩存也是一個(gè)map ,并且其value 存的是一個(gè)對(duì)象的工廠

private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);

我們可能已經(jīng)聽(tīng)說(shuō)過(guò)三級(jí)緩存放入的是Lambda表達(dá)式 的匿名函數(shù),這個(gè)函數(shù)會(huì)在使用到的時(shí)候被調(diào)用,那么spring 為什么選擇放一個(gè)匿名函數(shù)而不是直接放入一個(gè)bean 呢;顯然如果直接放入一個(gè)bean 那么三級(jí)緩存的作用就和二級(jí)緩存相同了;所以spring 這樣做肯定是有一些原因的,先來(lái)看下在原碼中三級(jí)緩存放入的Lambda是什么:

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    SmartInstantiationAwareBeanPostProcessor bp;
    if (!mbd.isSynthetic() && this.hasInstantiationAwareBeanPostProcessors()) {
        for(Iterator var5 = this.getBeanPostProcessorCache().smartInstantiationAware.iterator(); var5.hasNext(); exposedObject = bp.getEarlyBeanReference(exposedObject, beanName)) {
            bp = (SmartInstantiationAwareBeanPostProcessor)var5.next();
        }
    }

    return exposedObject;
}

從以上代碼可以執(zhí)行先把初始的 bean 對(duì)象賦給了 exposedObject ,然后如果發(fā)現(xiàn)這個(gè)bean 是否滿(mǎn)足mbd.isSynthetic() && this.hasInstantiationAwareBeanPostProcessors()條件,進(jìn)而判斷當(dāng)前的bean是否是一個(gè)合成的代理對(duì)象。

在AOP中,合成代理對(duì)象是通過(guò)特定的機(jī)制(如JDK動(dòng)態(tài)代理或CGLIB動(dòng)態(tài)代理)創(chuàng)建的。這個(gè)條件可以用來(lái)檢查當(dāng)前創(chuàng)建的bean是否是這種合成的代理對(duì)象;

如果需要合成代理對(duì)象則 進(jìn)入exposedObject = bp.getEarlyBeanReference(exposedObject, beanName) 進(jìn)行代理對(duì)象的創(chuàng)建: 

AbstractAutoProxyCreator.getEarlyBeanReference
   // 省略代碼
public Object getEarlyBeanReference(Object bean, String beanName) {
  Object cacheKey = this.getCacheKey(bean.getClass(), beanName);
     this.earlyProxyReferences.put(cacheKey, bean);
     return this.wrapIfNecessary(bean, beanName, cacheKey);
 }
Object exposedObject = bean;

 try {
 	// 遞歸填充改bean 的其他屬性
     this.populateBean(beanName, mbd, instanceWrapper);
     exposedObject = this.initializeBean(beanName, exposedObject, mbd);
 } catch (Throwable var18) {
     if (var18 instanceof BeanCreationException && beanName.equals(((BeanCreationException)var18).getBeanName())) {
         throw (BeanCreationException)var18;
     }

     throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", var18);
 }
   // 省略代碼
 protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
         return bean;
     } else if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
         return bean;
     } else if (!this.isInfrastructureClass(bean.getClass()) && !this.shouldSkip(bean.getClass(), beanName)) {
         Object[] specificInterceptors = this.getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, (TargetSource)null);
         if (specificInterceptors != DO_NOT_PROXY) {
             this.advisedBeans.put(cacheKey, Boolean.TRUE);
             Object proxy = this.createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
             this.proxyTypes.put(cacheKey, proxy.getClass());
             return proxy;
         } else {
             this.advisedBeans.put(cacheKey, Boolean.FALSE);
             return bean;
         }
     } else {
         this.advisedBeans.put(cacheKey, Boolean.FALSE);
         return bean;
     }
 }

從上面代碼可以看到 如果這個(gè)對(duì)象在緩存中沒(méi)有而且是必須要進(jìn)行依賴(lài)注入的, 會(huì)對(duì)實(shí)例化化后的改對(duì)象,使用this.createProxy 為其生產(chǎn)代理對(duì)象并進(jìn)行返回;所以使用了這個(gè)Lambda表達(dá)式目的就是返回一個(gè)普通對(duì)象的bean 或者代理對(duì)象的bean,那么為什么不直接在bean 實(shí)例化之后,就直接調(diào)用getEarlyBeanReference 方法,這樣將生成的普通對(duì)象或者代理對(duì)象的bean 直接放入到二級(jí)緩存中,這樣豈不是更為直接,顯然這樣做也是可以解決spring 的循環(huán)依賴(lài)的問(wèn)題,而且在二級(jí)緩存中存放的對(duì)象就是普通對(duì)象或者代理生成的對(duì)象。

雖然可以這樣做但是違反了spring 對(duì)代理對(duì)象生成的原則,Spring 的設(shè)計(jì)原則是盡可能保證普通對(duì)象創(chuàng)建完成之后,再生成其 AOP 代理(盡可能延遲代理對(duì)象的生成),因?yàn)檫@樣做的話(huà),所有代理都提前到了實(shí)例化之后,初始化階段前,顯然與盡可能延遲代理對(duì)象的生成 原則是違背的。所以在此使用 Lambda表達(dá)式 ,在真正需要?jiǎng)?chuàng)建對(duì)象bean 的提前引用時(shí),才通過(guò) Lambda表達(dá)式 來(lái)進(jìn)行創(chuàng)建 ,來(lái)遵循盡可能延遲代理對(duì)象的生成 原則。

沒(méi)有依賴(lài),有AOP 這種情況中,我們知道 AOP 代理對(duì)象的生成是在成品對(duì)象創(chuàng)建完成之后創(chuàng)建的,這也是 Spring 的設(shè)計(jì)原則,代理對(duì)象盡量推遲創(chuàng)建,循環(huán)依賴(lài) + AOP 這種情況中, 代理對(duì)象的生成提前了,因?yàn)楸仨氁WC其 AOP 功能,那么在bean 初始化完成之后,又到了要對(duì)改對(duì)象進(jìn)行代理增強(qiáng)的環(huán)節(jié),此時(shí)spring 又是怎么判斷改bean 已經(jīng)被增強(qiáng)為代理對(duì)象,而不需要重新創(chuàng)建代理對(duì)象?

public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
     if (bean != null) {
         Object cacheKey = this.getCacheKey(bean.getClass(), beanName);
         if (this.earlyProxyReferences.remove(cacheKey) != bean) {
             return this.wrapIfNecessary(bean, beanName, cacheKey);
         }
     }

     return bean;
 }
 public Object getEarlyBeanReference(Object bean, String beanName) {
        Object cacheKey = this.getCacheKey(bean.getClass(), beanName);
        this.earlyProxyReferences.put(cacheKey, bean);
        return this.wrapIfNecessary(bean, beanName, cacheKey);
    }
 

從以上源碼中可以看到 在postProcessAfterInitialization bean 被初始化完成之后執(zhí)行的方法 從this.getCacheKey 獲取到的cacheKey ,最后比較兩個(gè)bean 是否是一個(gè),如果是則說(shuō)明bean 已經(jīng)進(jìn)行過(guò)代理,否則則重新執(zhí)行wrapIfNecessary 生成代理對(duì)象;

2.2 三級(jí)緩存工作過(guò)程

既然在spring 容器中bean 是單例的,那么就不可能存在改bean 的多個(gè)對(duì)象,也即對(duì)bean 的所有引用都指向同一個(gè)對(duì)象;此時(shí)就有一個(gè)問(wèn)題,當(dāng)一個(gè)bean 被依賴(lài)注入時(shí),怎么知道這個(gè)單例的bean 是否已經(jīng)被初始化?

所以就需要將已經(jīng)完成初始化的bean 放入到一個(gè)地方中,這個(gè)地方要滿(mǎn)足如果這個(gè)bean 已經(jīng)被初始化過(guò)了,則不需要進(jìn)行在進(jìn)行初始化,而且存和取都比較方便,spring 選用map 來(lái)對(duì)其進(jìn)行存儲(chǔ),其中key 為bean的那么,value 為單列bean:

  private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);

當(dāng)Spring創(chuàng)建一個(gè)單例bean時(shí),會(huì)先檢查一級(jí)緩存(singletonObjects),如果在其中找到bean實(shí)例,則直接返回。如果沒(méi)有找到,則繼續(xù)執(zhí)行bean的創(chuàng)建流程,對(duì)改bean 進(jìn)行實(shí)例化,如果改bean允許早期暴露則將獲取改對(duì)象的一個(gè)方法即將工廠方法存儲(chǔ)在三級(jí)緩存(singletonFactories)中

在創(chuàng)建bean 如aservice的屬性注入過(guò)程中,如果發(fā)現(xiàn)還依賴(lài)了其它的bean,如:bservice 則進(jìn)入到bservice 的創(chuàng)建過(guò)程:首先對(duì)bservice 進(jìn)行實(shí)例化(如果bservice也運(yùn)行早期暴露,則也會(huì)被加入到第三級(jí)緩存中)、屬性注入、初始化;

如果此時(shí)bservice 中依賴(lài)了aservice 的bean,出現(xiàn)循環(huán)依賴(lài)問(wèn)題,先從一級(jí)緩存中獲取aservice 的bean,此時(shí)aservice的bean 是正在被創(chuàng)建的狀態(tài),則從二級(jí)緩存找 Spring會(huì)先嘗試從二級(jí)緩存(earlySingletonObjects)中獲取bean實(shí)例的早期引用,以解決循環(huán)依賴(lài),如果二級(jí)緩存中aservice 的早起引用不存在;則到三級(jí)緩存中,執(zhí)行aservice bean 的lamma 表達(dá)式,獲取到aservice 的普通對(duì)象/代理對(duì)象,并將其放入到第二級(jí)緩存,同時(shí)移除aservice 在第三級(jí)的緩存。

一旦bean創(chuàng)建完成,如:bservice 完成了依賴(lài)注入,初始化,aop(如果需要被代理)則會(huì)將其放入一級(jí)緩存(singletonObjects),并從二級(jí)緩存(earlySingletonObjects)和三級(jí)緩存(singletonFactories)中移除,即將bservice 放入到一級(jí)緩存(單例池中),同時(shí)從二級(jí)和三級(jí)緩存中移除bservice的早期應(yīng)用和 獲取早期引用的lamma 表達(dá)式。然后,在aservice 的屬性依賴(lài)注入中,就可以從一級(jí)緩存中獲取到bservice 的單例對(duì)象,進(jìn)行屬性注入,然后初始化,和aop(如果需要被aop代理),最后從二級(jí)和三級(jí)緩存中移除aservice的早期應(yīng)用和 獲取早期引用的lamma 表達(dá)式。

AbstractBeanFactory,doGetBean 獲取對(duì)應(yīng)的bean,此處只列舉關(guān)鍵代碼

protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException {
/**
** 省略代碼
**/ 
  // 會(huì)先檢查一級(jí)緩存(singletonObjects),如果在其中找到bean實(shí)例,則直接返回,
  // 此方法調(diào)用DefaultSingletonBeanRegistry 下getSingleton 方法
  // 方法(1) 從三級(jí)緩存中獲取對(duì)象
  Object sharedInstance = this.getSingleton(beanName);
  // 省略 sharedInstance 不為null 直接返回的代碼
  /**
** 省略代碼
**/ 
// 如果沒(méi)有找到,則繼續(xù)執(zhí)行bean的創(chuàng)建流程,并將工廠方法存儲(chǔ)在三級(jí)緩存(singletonFactories)中
 if (mbd.isSingleton()) {
 	// this.getSingleton 通過(guò)改bean 的工廠方法創(chuàng)建出來(lái)bean 并放入到單例池中
 	// 方法2:getSingleton(String beanName, ObjectFactory<?> singletonFactory) 創(chuàng)建對(duì)象
   sharedInstance = this.getSingleton(beanName, () -> {
        try {
        	// 創(chuàng)建普通對(duì)象/代理對(duì)象 并將工廠方法存儲(chǔ)在三級(jí)緩存(singletonFactories)中 
        	// 此方法調(diào)用  AbstractAutowireCapableBeanFactory 下 createBean 方法然后在調(diào)用 doCreateBean 方法
            return this.createBean(beanName, mbd, args);
        } catch (BeansException var5) {
            this.destroySingleton(beanName);
            throw var5;
        }
    });
    beanInstance = this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}

}

方法(1) 從三級(jí)緩存中獲取對(duì)象 this.getSingleton(beanName) 方法:

Object sharedInstance = this.getSingleton(beanName) 方法,DefaultSingletonBeanRegistrygetSingleton, 會(huì)先檢查一級(jí)緩存(singletonObjects),如果在其中找到bean實(shí)例,則直接返回:

@Nullable
public Object getSingleton(String beanName) {
     return this.getSingleton(beanName, true);
 }
 @Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 從一級(jí)緩存獲取完整的bean
  Object singletonObject = this.singletonObjects.get(beanName);
  // 沒(méi)有獲取到 并且 這個(gè)bean 正在初始化中
  if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
  	// 從二級(jí)緩存獲取這個(gè)bean 的早期引用
      singletonObject = this.earlySingletonObjects.get(beanName);
      // 沒(méi)有早期引用 并且運(yùn)行循環(huán)依賴(lài)
      if (singletonObject == null && allowEarlyReference) {
          synchronized(this.singletonObjects) {
          // 加對(duì)象鎖
              singletonObject = this.singletonObjects.get(beanName);
              if (singletonObject == null) {
                  singletonObject = this.earlySingletonObjects.get(beanName);
                  if (singletonObject == null) {
                  	// 從三級(jí)緩存 獲取bena 的 工廠類(lèi)
                      ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
                      if (singletonFactory != null) {
                          singletonObject = singletonFactory.getObject();
                          //  二級(jí)緩存中放入
                          this.earlySingletonObjects.put(beanName, singletonObject);
                          // 三級(jí)緩存中移除
                          this.singletonFactories.remove(beanName);
                      }
                  }
              }
          }
      }
  }

  return singletonObject;
}

單例池中沒(méi)有改bean 則進(jìn)入 sharedInstance = this.getSingleton(beanName, () -> {}) return this.createBean(beanName, mbd, args) 方法通過(guò)AbstractAutowireCapableBeanFactory.doCreateBean 創(chuàng)建普通對(duì)象/代理對(duì)象 并將工廠方法存儲(chǔ)在三級(jí)緩存(singletonFactories)中:

boolean earlySingletonExposure = mbd.isSingleton() && this.allowCircularReferences && this.isSingletonCurrentlyInCreation(beanName);
if (earlySingletonExposure) {
    if (this.logger.isTraceEnabled()) {
        this.logger.trace("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references");
    }
	//  addSingletonFactory 放入到三級(jí)緩存
    this.addSingletonFactory(beanName, () -> {
    	// 獲取提前暴露出來(lái)的bean 對(duì)象
        return this.getEarlyBeanReference(beanName, mbd, bean);
    });
}
try {
	// 繼續(xù)對(duì)改bean 中的屬性進(jìn)行初始化
    this.populateBean(beanName, mbd, instanceWrapper);
     exposedObject = this.initializeBean(beanName, exposedObject, mbd);
 } catch (Throwable var18) {
     if (var18 instanceof BeanCreationException && beanName.equals(((BeanCreationException)var18).getBeanName())) {
         throw (BeanCreationException)var18;
     }

     throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", var18);
 }

DefaultSingletonBeanRegistry.addSingletonFactory: bean的工廠放入到三級(jí)緩存:

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
       Assert.notNull(singletonFactory, "Singleton factory must not be null");
       synchronized(this.singletonObjects) {
           if (!this.singletonObjects.containsKey(beanName)) {
           		// 放入3級(jí)緩存map
               this.singletonFactories.put(beanName, singletonFactory);
               this.earlySingletonObjects.remove(beanName);
               // 將改bean 的名稱(chēng)放入到正在創(chuàng)建的bean 的set 集合
               this.registeredSingletons.add(beanName);
           }

       }
   }

方法2:getSingleton(String beanName, ObjectFactory<?> singletonFactory) 創(chuàng)建對(duì)象:

此時(shí)的singletonFactory 為 this.getSingleton(beanName, () -> {
try {
// 創(chuàng)建普通對(duì)象/代理對(duì)象 并將工廠方法存儲(chǔ)在三級(jí)緩存(singletonFactories)中
// 此方法調(diào)用 AbstractAutowireCapableBeanFactory 下 createBean 方法然后在調(diào)用 doCreateBean 方法
return this.createBean(beanName, mbd, args);
} catch (BeansException var5) {
this.destroySingleton(beanName);
throw var5;
}
}) 

方法中的lamma 表達(dá)式,通過(guò) singletonFactory.getObject() 就可以調(diào)用到 this.createBean(beanName, mbd, args) 方法

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
     Assert.notNull(beanName, "Bean name must not be null");
     synchronized(this.singletonObjects) {
     	//  獲取對(duì)象鎖,然后從單例池中獲取bean
         Object singletonObject = this.singletonObjects.get(beanName);
         if (singletonObject == null) {
            // 省略代碼

             try {
             	// 獲取bean 的工廠
                 singletonObject = singletonFactory.getObject();
                 newSingleton = true;
             } 
  			// 省略代碼
             if (newSingleton) {
               // bean 完成后放入到單例翅中
                 this.addSingleton(beanName, singletonObject);
             }
         }

         return singletonObject;
     }
 }
protected void addSingleton(String beanName, Object singletonObject) {
     synchronized(this.singletonObjects) {
     	// 單例池中放入改bean
         this.singletonObjects.put(beanName, singletonObject);
         // 從三級(jí)緩存和二級(jí)緩存中移除
         this.singletonFactories.remove(beanName);
         this.earlySingletonObjects.remove(beanName);
         this.registeredSingletons.add(beanName);
     }
 }

案例過(guò)程:

在A 依賴(lài)于B,B 依賴(lài)于C,C又依賴(lài)A 的場(chǎng)景中,當(dāng)實(shí)例化 A后對(duì)A 的bean 對(duì)象進(jìn)行屬性依賴(lài)注入時(shí);發(fā)現(xiàn)其依賴(lài)了B;此時(shí)實(shí)例化 B,然后對(duì)B的bean 對(duì)象進(jìn)行屬性依賴(lài)注入;發(fā)現(xiàn)依賴(lài)了C 的bean, 實(shí)例化 C,對(duì)C 的bean 進(jìn)行屬性注入時(shí),發(fā)現(xiàn)依賴(lài)了A的bean :

此時(shí)在對(duì)C的bean依賴(lài)注入為:先從單例池(一級(jí)緩存獲取A的bean),因?yàn)锳的bean 還在被創(chuàng)建過(guò)程中,所以從單例池中的到的是null,在從二級(jí)緩存(早期暴露的對(duì)象)獲取A的早期暴露bean,如果二級(jí)緩存也沒(méi)有,則從三級(jí)緩存中執(zhí)行l(wèi)amma 表達(dá)式,獲取到A的早期暴露bean (將A的bean 放入到二級(jí)緩存中,并同時(shí)從三級(jí)緩存中移除A的bean 的lamma 表達(dá)式)進(jìn)行屬性依賴(lài)注入;然后完成C的依賴(lài)注入,初始化 將其放入到單例池中,然后完成B的屬性依賴(lài)注入,初始化,將其放入到單例池;完成A 的屬性依賴(lài)注入,初始化,并放入到單例池中;

過(guò)程步驟:

  • 開(kāi)始初始化 A,創(chuàng)建 A 的實(shí)例并完成屬性注入;
  • 當(dāng)A屬性注入 過(guò)程中發(fā)現(xiàn) A 依賴(lài)于 B,Spring 會(huì)嘗試從單例池中獲取B的ean,如果獲取不到則進(jìn)入到創(chuàng)建 B 的實(shí)例過(guò)程;
  • 創(chuàng)建 B 的實(shí)例,對(duì)B 的bean進(jìn)行依賴(lài)注入,發(fā)現(xiàn) B 還有其他依賴(lài),如: C。此時(shí) Spring 會(huì)先從單例池中獲取C的bean,如果獲取不到則進(jìn)入到C的bean 創(chuàng)建過(guò)程;;
  • 在C實(shí)例化后,對(duì)C屬性依賴(lài)注入 過(guò)程中,發(fā)現(xiàn) C 依賴(lài)于 A,從單例池中獲取A 的bean,此時(shí) A 實(shí)例還未創(chuàng)建完成,從二級(jí)緩存獲取A bean 的早期引用;如果還沒(méi)有,則從三級(jí)緩存找到A bean的lamma 表達(dá)式,獲取到Abean 的早期暴露對(duì)象,并將Abean的早期暴露對(duì)象,放入到二級(jí)緩存同時(shí),將Abean從三級(jí)緩存中移除;
  • 繼續(xù)完成對(duì) C 屬性注入(如果有),然后進(jìn)行初始化,最終將C的bean 放入到單例池中,同時(shí)刪除其在二級(jí)和三級(jí)緩存的對(duì)象;
  • 然后完成對(duì)B 其它屬性依賴(lài)注入中,發(fā)現(xiàn) B 不再依賴(lài)其他對(duì)象,完成 B 的初始化,最終將B的bean 放入到單例池中,同時(shí)刪除其在二級(jí)和三級(jí)緩存的對(duì)象;
  • 繼續(xù) A的依賴(lài)注入,在 A的依賴(lài)注入 過(guò)程中,發(fā)現(xiàn) A 依賴(lài)于 B,B 的實(shí)例早已創(chuàng)建完成,因此可以直接從一級(jí)緩存獲取 B 的實(shí)例;
  • 完成 A 的依賴(lài)注入,初始化 ,最終將A的bean 放入到單例池中,同時(shí)刪除其在二級(jí)和三級(jí)緩存的對(duì)象

Spring 使用了緩存機(jī)制,確保在遞歸調(diào)用時(shí)能夠正確地獲取到已經(jīng)創(chuàng)建的對(duì)象實(shí)例,避免死循環(huán)。同時(shí),Spring 也會(huì)處理代理對(duì)象的生成和使用,以確保 A、B、C 的代理對(duì)象在正確的時(shí)間被創(chuàng)建和使用。

三、Spring 三級(jí)緩存無(wú)法解決的單例循環(huán)依賴(lài)情況

3.1 通過(guò)構(gòu)造方法注入的bean ,出現(xiàn)循環(huán)依賴(lài)會(huì)報(bào)錯(cuò)

在A類(lèi)中通過(guò)構(gòu)造方法的方式注入B的bean,在B類(lèi)中通過(guò)構(gòu)造方法的方式注入A的bean;在此場(chǎng)景中,

  • 在調(diào)用A的構(gòu)造方法進(jìn)行實(shí)例化時(shí),發(fā)現(xiàn)依賴(lài)的B的bean,需要對(duì)B類(lèi)進(jìn)行實(shí)例化;
  • 調(diào)用B類(lèi)的構(gòu)造方法進(jìn)行實(shí)例化時(shí),發(fā)現(xiàn)依賴(lài)的A的bean,此時(shí)出現(xiàn)循環(huán)依賴(lài);
  • 然后此時(shí)需要對(duì)A的bean 提前進(jìn)行引用的暴露;
  • 然而在對(duì)A的bean 提前進(jìn)行引用的暴露,需要用到A 的實(shí)例化對(duì)象,此時(shí)A的實(shí)例化對(duì)象還沒(méi)有被創(chuàng)建,則直接報(bào)錯(cuò);

3.2 早期暴露的非aop代理對(duì)象引用,出現(xiàn)循環(huán)依賴(lài)會(huì)報(bào)錯(cuò)

@Service
public class AserviceImpl implements Aservice {
    @Autowired
    private Bservice bservice;

    @Async
    public  void test(){
        System.out.println(bservice);
    }

}
@Service
public class BserviceImpl implements Bservice {

    @Autowired
    private Aservice aservice;

}

當(dāng)使用 @Async 注解標(biāo)注一個(gè)bean 中的方法為異步方法時(shí),Bservice 中注入的Aservice aservice 的bean 與最終生成的Aservice 的bean 不相同而導(dǎo)致報(bào)錯(cuò);

  • 對(duì)Aservice 進(jìn)行實(shí)例化后,對(duì)其bservice 屬性進(jìn)行初始化;
  • 對(duì)Bservice 進(jìn)行實(shí)例化,然后對(duì)其aservice 屬性進(jìn)行初始化,此時(shí)發(fā)現(xiàn)循環(huán)依賴(lài);
  • 暴露Aservice 的bean 到二級(jí)緩存中,因?yàn)锳service 非aop 代理對(duì)象 ,所以此時(shí)二級(jí)緩存中放入的是Aservice 的普通對(duì)象;
  • Bservice 的bean 完成初始化;
  • Aservice 對(duì) bservice 屬性初始化完成 ,并將Aservice 的bean 放入到一級(jí)緩存,并從二級(jí)緩存中刪除;
  • 對(duì)Aservice 的bean 進(jìn)行代理對(duì)象的包裝,包裝后的bean 與之前放入到一級(jí)緩存的bean 兩個(gè)不是同一個(gè),程序報(bào)錯(cuò);

四、Lazy 注解解決循環(huán)依賴(lài)問(wèn)題

延遲加載可以通過(guò)將 bean 的依賴(lài)關(guān)系運(yùn)行時(shí)進(jìn)行注入,而不是在初始化階段。這樣,當(dāng)遇到循環(huán)依賴(lài)時(shí),Spring 可以先創(chuàng)建需要的 bean 實(shí)例,并將其設(shè)置為代理對(duì)象,而不需要立即解決依賴(lài)關(guān)系。

@Service
public class AserviceImpl implements Aservice {
    @Autowired
    @Lazy
    private Bservice bservice;

    @Async
    public  void test(){
    }

}

Lazy 延遲加載打破循環(huán)依賴(lài); 通過(guò)其它途徑生成bservice 的lazy 的代理對(duì)象,不會(huì)去走創(chuàng)建bservice 的代理 對(duì)象 然后注入aservice 這套流程。這樣創(chuàng)建aservice 的單例對(duì)象并放入到單例池中,Bservice 的bean 在實(shí)例化后,注入aservice bean 屬性就可以從單例池中加載到aservice 的真正的bean ,而不會(huì)出現(xiàn)bean 對(duì)象不一致的問(wèn)題。

總結(jié)

spring 通過(guò)三級(jí)緩存解決單例的循環(huán)依賴(lài)問(wèn)題,singletonObjects 用來(lái)存放已經(jīng)初始化完成的bean,earlySingletonObjects 用來(lái)存放早期暴露出來(lái)的半成品bean 的引用,singletonFactories 用來(lái)存放獲取早期引用的 Lambda表達(dá)式 工廠。

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

最新評(píng)論