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

Java中的Spring?如何處理循環(huán)依賴(lài)

 更新時(shí)間:2022年05月22日 08:51:59   作者:??Java架構(gòu)師????  
這篇文章主要介紹了Java中的Spring?如何處理循環(huán)依賴(lài),依賴(lài)指的是Bean與Bean之間的依賴(lài)關(guān)系,循環(huán)依賴(lài)指的是兩個(gè)或者多個(gè)Bean相互依賴(lài),關(guān)于更多Spring?處理循環(huán)依賴(lài)的詳情,需要的朋友可以參考下面文章具體內(nèi)容

前言

Spring如何處理循環(huán)依賴(lài)?這是最近較為頻繁被問(wèn)到的一個(gè)面試題,在前面Bean實(shí)例化流程中,對(duì)屬性注入一文多多少少對(duì)循環(huán)依賴(lài)有過(guò)介紹,這篇文章詳細(xì)講一下Spring中的循環(huán)依賴(lài)的處理方案。

什么是循環(huán)依賴(lài)

依賴(lài)指的是Bean與Bean之間的依賴(lài)關(guān)系,循環(huán)依賴(lài)指的是兩個(gè)或者多個(gè)Bean相互依賴(lài),

構(gòu)造器循環(huán)依賴(lài)

代碼示例:

public class BeanA {
    private BeanB beanB;
    public BeanA(BeanB beanB){
        this.beanB = beanB;
    }
}
public class BeanB {

    private BeanA beanA;

    public BeanB(BeanA beanA){
        this.beanA = beanA;
    }
}

配置文件:

<bean id="beanA" class="cn.itsource._01_di.BeanA" >
        <constructor-arg type="cn.itsource._01_di.BeanB" ref="beanB"  />
 </bean>

 <bean id="beanB" class="cn.itsource._01_di.BeanB"  >
         <constructor-arg type="cn.itsource._01_di.BeanA" ref="beanA" />
 </bean>

Setter循環(huán)依賴(lài)

代碼示例:

public class BeanA {
    private BeanB beanB;
    public void setBeanB(BeanB beanB){
        this.beanB = beanB;
    }
}
@Data
public class BeanB {

    private BeanA beanA;

    public void setBeanA(BeanA beanA){
        this.beanA = beanA;
    }
}

配置文件

<bean id="beanA" class="cn.itsource._01_di.BeanA" >
    <property name="beanB" ref="beanB" />
</bean>
<bean id="beanB" class="cn.itsource._01_di.BeanB">
    <property name="beanA" ref="beanA" />
</bean>

循環(huán)依賴(lài)包括: 構(gòu)造器注入循環(huán)依賴(lài) set , 注入循環(huán)依賴(lài) 和 prototype模式Bean的循環(huán)依賴(lài)。Spring只解決了單例Bean的 setter 注入循環(huán)依賴(lài),對(duì)于構(gòu)造器循環(huán)依賴(lài),和 prototype模式的循環(huán)依賴(lài)是無(wú)法解決的,在創(chuàng)建Bean的時(shí)候就會(huì)拋出異常 :“BeanCurrentlyInCreationException” ,

循環(huán)依賴(lài)控制開(kāi)關(guān)在 AbstractRefreshableApplicationContext 容器工廠(chǎng)類(lèi)中有定義:

public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext {
 @Nullable
 private Boolean allowBeanDefinitionOverriding;
 //是否允許循環(huán)依賴(lài)
 @Nullable
 private Boolean allowCircularReferences;

 //設(shè)置循環(huán)依賴(lài)
 public void setAllowCircularReferences(boolean allowCircularReferences) {
  this.allowCircularReferences = allowCircularReferences;
 }

默認(rèn)情況下是允許Bean之間的循環(huán)依賴(lài)的,在依賴(lài)注入時(shí)Spring會(huì)嘗試處理循環(huán)依賴(lài)。如果將該屬性配置為“false”則關(guān)閉循環(huán)依賴(lài),當(dāng)在Bean依賴(lài)注入的時(shí)遇到循環(huán)依賴(lài)時(shí)拋出異常??梢酝ㄟ^(guò)如下方式關(guān)閉,但是一般都不這么做

ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
//禁用循環(huán)依賴(lài)
applicationContext.setAllowCircularReferences(false);
//刷新容器
applicationContext.refresh();
...

構(gòu)造器循環(huán)依賴(lài)處理

構(gòu)造器是不允許循環(huán)依賴(lài)的,動(dòng)動(dòng)你的小腦瓜想一想,比如:A 依賴(lài) B ,B依賴(lài)C,C依賴(lài)A,在實(shí)例化A的時(shí)候,構(gòu)造器需要注入B,然后Spirng會(huì)實(shí)例化B,此時(shí)的A屬于“正在創(chuàng)建”的狀態(tài)。當(dāng)實(shí)例化B的時(shí)候,發(fā)現(xiàn)構(gòu)造器需要注入C,然后去實(shí)例化C,然而實(shí)例化C的時(shí)候又需要注入A的實(shí)例,這樣就造成了一個(gè)死循環(huán),永遠(yuǎn)無(wú)法先實(shí)例化出某一個(gè)Bean,所以Spring遇到這里構(gòu)造器循環(huán)依賴(lài)會(huì)直接拋出異常。

那么Spring到底是如何做的呢?

  • 首先Spring會(huì)走Bean的實(shí)例化流程嘗試創(chuàng)建 A 的實(shí)例 ,在創(chuàng)建實(shí)例之間先從 “正在創(chuàng)建Bean池” (一個(gè)緩存Map而已)中去查找A 是否正在創(chuàng)建,如果沒(méi)找到,則將 A 放入 “正在創(chuàng)建Bean池”中,然后準(zhǔn)備實(shí)例化構(gòu)造器參數(shù) B。
  • Spring會(huì)走Bean的實(shí)例化流程嘗試創(chuàng)建 B 的實(shí)例 ,在創(chuàng)建實(shí)例之間先從 “正在創(chuàng)建Bean池” (一個(gè)緩存Map而已)中去查找B 是否正在創(chuàng)建,如果沒(méi)找到,則將 B 放入 “正在創(chuàng)建Bean池”中,然后準(zhǔn)備實(shí)例化構(gòu)造器參數(shù) A。
  • Spring會(huì)走Bean的實(shí)例化流程嘗試創(chuàng)建 A 的實(shí)例 ,在創(chuàng)建實(shí)例之間先從 “正在創(chuàng)建Bean池” (一個(gè)緩存Map而已)中去查找A 是否正在創(chuàng)建。
  • 此時(shí):Spring發(fā)現(xiàn) A 正處于“正在創(chuàng)建Bean池”,表示出現(xiàn)構(gòu)造器循環(huán)依賴(lài),拋出異常:“BeanCurrentlyInCreationException”

DefaultSingletonBeanRegistry#getSingleton

下面我們以 BeanA 構(gòu)造參數(shù)依賴(lài)BeanB, BeanB 構(gòu)造參數(shù)依賴(lài)BeanA 為例來(lái)分析。

當(dāng)Spring的IOC容器啟動(dòng),嘗試對(duì)單利的BeanA進(jìn)行初始化,根據(jù)之前的分析我們知道,單利Bean的創(chuàng)建入口是 AbstractBeanFactory#doGetBean 在該方法中會(huì)先從單利Bean緩存中獲取,如果沒(méi)有代碼會(huì)走到:DefaultSingletonBeanRegistry#getSingleton(jString beanName, ObjectFactory<?> singletonFactory) 方法中 ,在該方法中會(huì)先對(duì)把創(chuàng)建的Bean加入 一個(gè)名字為 singletonsCurrentlyInCreation 的 ConcurrentHashMap中,意思是該Bean正在創(chuàng)建中,然后調(diào)用 ObjectFactory.getObject() 實(shí)例化Bean , 假設(shè) BeanA 進(jìn)入了該方法進(jìn)行實(shí)例化:

//正在創(chuàng)建中的Bean
private final Set<String> singletonsCurrentlyInCreation =
   Collections.newSetFromMap(new ConcurrentHashMap<>(16));
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
  ...省略...
  //把該Bean的名字加入 singletonsCurrentlyInCreation 正在創(chuàng)建池 中
  beforeSingletonCreation(beanName);
  boolean newSingleton = false;
  boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
  if (recordSuppressedExceptions) {
   this.suppressedExceptions = new LinkedHashSet<>();
  }
  try {
   //調(diào)用ObjectFactory創(chuàng)建Bean的實(shí)例
   singletonObject = singletonFactory.getObject();
   newSingleton = true;
  }
...省略...

//如果singletonsCurrentlyInCreation中沒(méi)該Bean,就把該Bean存儲(chǔ)到singletonsCurrentlyInCreation中,
//如果 singletonsCurrentlyInCreation 中有 該Bean,就報(bào)錯(cuò)循環(huán)依賴(lài)異常BeanCurrentlyInCreationException
//也就意味著同一個(gè)beanName進(jìn)入該方法2次就會(huì)拋異常
protected void beforeSingletonCreation(String beanName) {
  if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
   throw new BeanCurrentlyInCreationException(beanName);
  }
 }

beforeSingletonCreation 方法非常關(guān)鍵 ,它會(huì)把beanName加入 singletonsCurrentlyInCreation,一個(gè)代表“正在創(chuàng)建中的Bean”的ConcurrentHashMap中。

如果singletonsCurrentlyInCreation中沒(méi)該beanName,就把該Bean存儲(chǔ)到singletonsCurrentlyInCreation中, 如果 singletonsCurrentlyInCreation 中有 該Bean,就報(bào)錯(cuò)循環(huán)依賴(lài)異常BeanCurrentlyInCreationException

【注意】也就意味著同一個(gè)beanName進(jìn)入該方法2次就會(huì)拋異常 , 現(xiàn)在BeanA已經(jīng)加入了singletonsCurrentlyInCreation

AbstractAutowireCapableBeanFactory#autowireConstructor

我們前面分析過(guò) ObjectFactory.getObject實(shí)例化Bean的詳細(xì)流程,這里我只是大概在復(fù)盤(pán)一下就行了。因?yàn)槲覀兊腂eanA的構(gòu)造器注入了一個(gè)BeanB,所以 代碼最終會(huì)走到AbstractAutowireCapableBeanFactory#autowireConstructor ,通過(guò)構(gòu)造器來(lái)實(shí)例化BeanA(在屬性注入那一章有講到 ) 。

在autowireConstructor 方法中會(huì)通過(guò) ConstructorResolver#resolveConstructorArguments來(lái)解析構(gòu)造參數(shù),調(diào)用 BeanDefinitionValueResolver 去把 ref="beanB" 這種字符串的引用變成一個(gè)實(shí)實(shí)在在的Bean,即BeanB,所以在 BeanDefinitionValueResolver 屬性值解析器中又會(huì)去實(shí)例化BeanB,同樣會(huì)走到 DefaultSingletonBeanRegistry#getSingleton 中把BeanB加入 singletonsCurrentlyInCreation “正在創(chuàng)建Bean池”中,然后調(diào)用ObjectFactory.getObject實(shí)例化BeanB。

低于BeanB而已同樣需要通過(guò)構(gòu)造器創(chuàng)建,BeanB構(gòu)造器參數(shù)依賴(lài)了BeanA,也就意味著又會(huì)調(diào)用 BeanDefinitionValueResolver 去把 ref=“beanA” 這種字符串引用變成容器中的BeanA的Bean實(shí)例,然后代碼又會(huì)走到 DefaultSingletonBeanRegistry#getSingleton。然后再一次的嘗試把BeanA加入singletonsCurrentlyInCreation “正在創(chuàng)建Bean池”。

此時(shí)問(wèn)題就來(lái)了,在最開(kāi)始創(chuàng)建BeanA的時(shí)候它已經(jīng)加入過(guò)一次“正在創(chuàng)建Bean” 池,這會(huì)兒實(shí)例化BeanB的時(shí)候,由于構(gòu)造器參數(shù)依賴(lài)了BeanA,導(dǎo)致BeanA又想進(jìn)入“正在創(chuàng)建Bean” 池 ,此時(shí) Spring拋出循環(huán)依賴(lài)異常:

Error creating bean with name ‘beanA’: Requested bean is currently in creation: Is there an unresolvable circular reference?

到這,Spring處理構(gòu)造器循環(huán)依賴(lài)的源碼分析完畢。

setter循環(huán)依賴(lài)處理

setter循環(huán)依賴(lài)是可以允許的。Spring是通過(guò)提前暴露未實(shí)例化完成的Bean的 ObjectFactory來(lái)實(shí)現(xiàn)循環(huán)依賴(lài)的,這樣做的目的是其他的Bean可以通過(guò) ObjectFactory 引用到該Bean。

實(shí)現(xiàn)流程如下:

  • Spring創(chuàng)建BeanA,通過(guò)無(wú)參構(gòu)造實(shí)例化,把BeanA添加到“正在創(chuàng)建Bean池”中,并暴露當(dāng)前實(shí)例的ObjectFactory,即把ObjectFactory添加到singletonFactories(三級(jí)緩存)中,該ObjectFactory用來(lái)獲取創(chuàng)建中的BeanA,然后,然后通過(guò)setter注入BeanB
  • Spring創(chuàng)建BeanB,通過(guò)無(wú)參構(gòu)造實(shí)例化,把BeanB添加到“正在創(chuàng)建Bean池”中,并暴露一個(gè)ObjectFactory,然后,然后通過(guò)setter注入BeanA
  • 在BeanB通過(guò)setter注入BeanA時(shí),由于BeanA 提前暴露了ObjectFactory ,通過(guò)它返回一個(gè)提前暴露一個(gè)創(chuàng)建中的BeanA。
  • 然后完成BeanB的依賴(lài)注入

獲取Bean的時(shí)候走三級(jí)緩存:

 protected Object getSingleton(String beanName, boolean allowEarlyReference) {
     //一級(jí)緩存,存儲(chǔ)實(shí)例化好的Bean
  Object singletonObject = this.singletonObjects.get(beanName);
  //如果單利緩存池中沒(méi)有,但是beanName正在創(chuàng)建
  if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
   synchronized (this.singletonObjects) {
       //獲取二級(jí)緩存,這個(gè)里面存儲(chǔ)的是正在創(chuàng)建的Bean,半成品
    singletonObject = this.earlySingletonObjects.get(beanName);
     //如果也為空,但是允許循環(huán)依賴(lài)
    if (singletonObject == null && allowEarlyReference) {
     //從三級(jí)緩存獲取Bean的創(chuàng)建工廠(chǎng),
     ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
     if (singletonFactory != null) {
        //創(chuàng)建Bean的實(shí)例
      singletonObject = singletonFactory.getObject();
      //把Bean存儲(chǔ)到二級(jí)緩存
      this.earlySingletonObjects.put(beanName, singletonObject);
       //移除三級(jí)緩存中的創(chuàng)建工廠(chǎng)
      this.singletonFactories.remove(beanName);
     }
    }
   }
  }
  return (singletonObject != NULL_OBJECT ? singletonObject : null);
 }

AbstractAutowireCapableBeanFactory#doCreateBean

我們以BeanA 通過(guò)settter依賴(lài)BeanB,BeanB通過(guò)setter 依賴(lài)BeanA為例來(lái)分析一下源碼,在之前的Bean實(shí)例化流程分析過(guò)程中我們了解到,Bean的實(shí)例化會(huì)走AbstractBeanFactory#doGetBean,然后查找單利緩存中是否有該Bean ,如果沒(méi)有就調(diào)用 DefaultSingletonBeanRegistry#getSingleton,方法會(huì)把BeanA加入 singletonsCurrentlyInCreation “創(chuàng)建中的Bean池”,然后調(diào)用ObjectFactory.getObject創(chuàng)建Bean.

org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean 源碼:

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
   @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
  final String beanName = transformedBeanName(name);
  Object bean;
  // Eagerly check singleton cache for manually registered singletons.
  //緩存中獲取Bean,解決了循環(huán)依賴(lài)問(wèn)題
  Object sharedInstance = getSingleton(beanName);
      ...緩存中沒(méi)有走下面...
  if (mbd.isSingleton()) {
     //走 DefaultSingletonBeanRegistry#getSingleton ,方法會(huì)把bean加入“正在創(chuàng)建bean池”
     //然后調(diào)用ObjectFactory實(shí)例化Bean
     sharedInstance = getSingleton(beanName, () -> {
      try {
       return createBean(beanName, mbd, args);
      }
      catch (BeansException ex) {
       // Explicitly remove instance from singleton cache: It might have been put there
       // eagerly by the creation process, to allow for circular reference resolution.
       // Also remove any beans that received a temporary reference to the bean.
       destroySingleton(beanName);
       throw ex;
      }
     });
     bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
    }

第一次進(jìn)來(lái),緩存中是沒(méi)有BeanA的,所有會(huì)走 getSingleton 方法,然后代碼最終會(huì)走到AbstractAutowireCapableBeanFactory#doCreateBean 方法中 。

AbstractAutowireCapableBeanFactory#doCreateBean源碼:

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
   throws BeanCreationException {
  // Instantiate the bean.
  BeanWrapper instanceWrapper = null;
  if (mbd.isSingleton()) {
   instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
  }
  if (instanceWrapper == null) {
  //實(shí)例化Bean
   instanceWrapper = createBeanInstance(beanName, mbd, args);
  }
  ...省略...
  //如果是單利 ,如果是允許循環(huán)依賴(lài),如果 beanName 出于創(chuàng)建中,已經(jīng)被添加到“創(chuàng)建中的bean池”
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
    isSingletonCurrentlyInCreation(beanName));
  if (earlySingletonExposure) {
   if (logger.isDebugEnabled()) {
    logger.debug("Eagerly caching bean '" + beanName +
      "' to allow for resolving potential circular references");
   }
   //把ObjectFactory 添加到 singletonFactories 中。
   addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
  }

 try {
  //走依賴(lài)注入流程
   populateBean(beanName, mbd, instanceWrapper);
   exposedObject = initializeBean(beanName, exposedObject, mbd);
  }

//緩存單利Bean的創(chuàng)建工廠(chǎng),用于解決循環(huán)依賴(lài)
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
  Assert.notNull(singletonFactory, "Singleton factory must not be null");
  synchronized (this.singletonObjects) {
   //singletonObjects單利緩存中是否包含Bean
   if (!this.singletonObjects.containsKey(beanName)) {
    //提前暴露ObjectFactory,把ObjectFactory放到singletonFactories中,
    //后面解決循環(huán)依賴(lài),獲取Bean實(shí)例的時(shí)候會(huì)用到
    this.singletonFactories.put(beanName, singletonFactory);
    //早期單利bean緩存中移除Bean
    this.earlySingletonObjects.remove(beanName);
    //把注冊(cè)的Bean加入registeredSingletons中
    this.registeredSingletons.add(beanName);
   }
  }
 }

該方法中把BeanA實(shí)例化好之后,會(huì)把ObjectFactory存儲(chǔ)到一個(gè) singletonFactories(HashMap)中來(lái)提前暴露Bean的創(chuàng)建工廠(chǎng),用于解決循環(huán)依賴(lài)【重要】,然后調(diào)用 populateBean 走屬性注入流程。

屬性注入會(huì)通過(guò)BeanDefinition得到bean的依賴(lài)屬性,然后調(diào)用 AbstractAutowireCapableBeanFactory#applyPropertyValues ,把屬性應(yīng)用到對(duì)象上。在applyPropertyValues 方法中最終調(diào)用 BeanDefinitionValueResolver#resolveValueIfNecessary 解析屬性值,比如:ref=“beanB” 這種字符串引用變成 對(duì)象實(shí)例的引用。

在BeanDefinitionValueResolver解析依賴(lài)的屬性值即:BeanB的時(shí)候,同樣會(huì)觸發(fā)BeanB的實(shí)例化,代碼會(huì)走到AbstractBeanFactory#doGetBean ,然后走方法 DefaultSingletonBeanRegistry#getSingleton 中把BeanB加入 singletonsCurrentlyInCreation “創(chuàng)建中的Bean池”,然后代碼會(huì)走到AbstractAutowireCapableBeanFactory#doCreateBean 方法中創(chuàng)建BeanB,

該方法中會(huì)先實(shí)例化BeanB,接著會(huì)把BeanB的ObjectFactory存儲(chǔ)到 singletonFactories (HashMap)中來(lái)提前暴露Bean的創(chuàng)建工廠(chǎng),用于解決循環(huán)依賴(lài),然后調(diào)用 populateBean 走屬性注入流程。

同樣因?yàn)锽eanB通過(guò)Setter 注入了 A,所以在 populateBean 屬性注入流程中會(huì)解析 ref=“beanA” 為容器中的 BeanA 的實(shí)例。

然后會(huì)走到 AbstractBeanFactory#doGetBean 中獲取BeanA的實(shí)例。這個(gè)時(shí)候流程就不一樣了,我們先看一下 AbstractBeanFactory#doGetBean 中的代碼

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
   @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
  final String beanName = transformedBeanName(name);
  Object bean;
  // Eagerly check singleton cache for manually registered singletons.
  //從緩存中獲取Bean
  Object sharedInstance = getSingleton(beanName);
  ...省略...
  //如果緩存中沒(méi)有Bean,就創(chuàng)建Bean
  if (mbd.isSingleton()) {
     sharedInstance = getSingleton(beanName, () -> {
      try {
       return createBean(beanName, mbd, args);
      }
      catch (BeansException ex) {
       // Explicitly remove instance from singleton cache: It might have been put there
       // eagerly by the creation process, to allow for circular reference resolution.
       // Also remove any beans that received a temporary reference to the bean.
       destroySingleton(beanName);
       throw ex;
      }
     });
     bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
    }

在獲取單利Bean的實(shí)例的時(shí)候是會(huì)先去單利Bean的緩存中去查看Bean是否已經(jīng)存在,如果不存在,才會(huì)走DefaultSingletonBeanRegistry#getSingleton方法創(chuàng)建Bean。

問(wèn)題是:此刻單利Bean緩存中已經(jīng)有BeanA了,因?yàn)樵谧铋_(kāi)始BeanA已經(jīng)出于“正在創(chuàng)建Bean池”中了。我們先來(lái)看一下是如何從緩存獲取Bean的。

DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)源碼如下:

//allowEarlyReference :是否創(chuàng)建早期應(yīng)用,主要用來(lái)解決循環(huán)依賴(lài)
 @Nullable
 protected Object getSingleton(String beanName, boolean allowEarlyReference) {
  // Quick check for existing instance without full singleton lock
  //從Map中 singletonObjects = new ConcurrentHashMap<>(256); 獲取單利Bean
  //【一級(jí)緩存】singletonObject緩存中是否有Bean , 它存儲(chǔ)的是已經(jīng)實(shí)例化好的Bean
  Object singletonObject = this.singletonObjects.get(beanName);
  //如果singletonObjects中沒(méi)有Bean,但是Bean出于正在創(chuàng)建池中,即:Set<String> singletonsCurrentlyInCreation中有Bean,
  if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {

   //【二級(jí)緩存】從早期單例對(duì)象的緩存 earlySingletonObjects 中獲取
   singletonObject = this.earlySingletonObjects.get(beanName);

   //早期單利對(duì)象緩存中也沒(méi)有,但是允許循環(huán)依賴(lài)
   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) {

       //【三級(jí)緩存】獲取ObjectFactory , 對(duì)象創(chuàng)建工廠(chǎng),得到Bean創(chuàng)建過(guò)程中提前暴露的工廠(chǎng)。
       ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
       if (singletonFactory != null) {
        //通過(guò)工廠(chǎng)ObjectFactory 獲取對(duì)象實(shí)例
        singletonObject = singletonFactory.getObject();
        //把對(duì)象存儲(chǔ)到早期緩存中
        this.earlySingletonObjects.put(beanName, singletonObject);
        //把ObjectFactory移除
        this.singletonFactories.remove(beanName);
       }
      }
     }
    }
   }
  }
  return singletonObject;
 }

這里就是經(jīng)典的三級(jí)緩存解決Spring循環(huán)依賴(lài)。你看到了,這里會(huì)先從 singletonObjects 單利Bean緩存集合中獲取Bean(該緩存是實(shí)例化完成了的Bean),如果沒(méi)有,就從earlySingletonObjects早期對(duì)象緩存中獲取Bean(該緩存中存放的是還未實(shí)例化完成的早期Bean),如果還是沒(méi)有,就從singletonFactories中得到暴露的ObjectFactory來(lái)獲取依賴(lài)的Bean。然后放入早期緩存中。并把ObjectFactory從singletonFactories中移除。最后返回Bean的實(shí)例。

由于在實(shí)例化BeanA的時(shí)候已經(jīng)把BeanA的ObjectFactory添加到了 singletonFactories 緩存中,那么這里就會(huì)走到 singletonFactory.getObject(); 方法得到BeanA的實(shí)例,并且會(huì)把BeanA存儲(chǔ)到 earlySingletonObjects早期單利Bean緩存中。

BeanA的實(shí)例成功返回,那么BeanB的 setter注入成功,代表BeanB實(shí)例化完成,那么BeanA的setter方法注入成功,BeanA實(shí)例化完成。

prototype模式的循環(huán)依賴(lài)

對(duì)于prototype模式下的Bean不允許循環(huán)依賴(lài),因?yàn)?這種模式下Bean是不做緩存的,所以就沒(méi)法暴露ObjectFactory,也就沒(méi)辦法實(shí)現(xiàn)循環(huán)依賴(lài)。

總結(jié)

不知道你有沒(méi)有看暈,反正我但是在源碼時(shí)的過(guò)程是比較辛苦的,這里需要你對(duì)前面Bean的實(shí)例化流程和屬性注入流程比較熟悉,否則就會(huì)暈菜。

這里總結(jié)一下:

構(gòu)造器循環(huán)依賴(lài)是不允許的,主要通過(guò) singletonsCurrentlyInCreation “正在創(chuàng)建Bean池” 把創(chuàng)建中的Bean緩存起來(lái),如果循環(huán)依賴(lài),同一個(gè)Bean勢(shì)必會(huì)嘗試進(jìn)入該緩存2次,拋出循環(huán)依賴(lài)異常。

setter循環(huán)依賴(lài)是可以允許的。Spring是通過(guò)提前暴露未實(shí)例化完成的Bean的 ObjectFactory來(lái)實(shí)現(xiàn)循環(huán)依賴(lài)的,這樣做的目的是其他的Bean可以通過(guò) ObjectFactory 引用到該Bean 。在獲取依賴(lài)的Bean的時(shí)候使用到了三級(jí)緩存。

到此這篇關(guān)于Java中的Spring 如何處理循環(huán)依賴(lài)的文章就介紹到這了,更多相關(guān)Spring 循環(huán)依賴(lài)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Spring security實(shí)現(xiàn)對(duì)賬戶(hù)進(jìn)行加密

    Spring security實(shí)現(xiàn)對(duì)賬戶(hù)進(jìn)行加密

    這篇文章主要介紹了Spring security實(shí)現(xiàn)對(duì)賬戶(hù)進(jìn)行加密,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-03-03
  • 5分鐘快速了解String.trim()到底做了什么事

    5分鐘快速了解String.trim()到底做了什么事

    trim方法一般用來(lái)去除空格,但是根據(jù)JDK API的說(shuō)明,該方法并不僅僅是去除空格,它能夠去除從編碼'\u0000'至'\u0020'的所有字符,這篇文章主要給大家介紹了如何通過(guò)5分鐘快速了解String.trim()到底做了什么事,需要的朋友可以參考下
    2021-11-11
  • Spring-cloud-eureka使用feign調(diào)用服務(wù)接口

    Spring-cloud-eureka使用feign調(diào)用服務(wù)接口

    這篇文章主要為大家詳細(xì)介紹了Spring-cloud-eureka使用feign調(diào)用服務(wù)接口,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-04-04
  • java7改善后的異常處理

    java7改善后的異常處理

    在本篇文章里小編給大家整理的是關(guān)于java7改善后的異常處理知識(shí)點(diǎn)總結(jié),有需要的朋友們參考下。
    2019-11-11
  • SpringBoot?項(xiàng)目中創(chuàng)建線(xiàn)程池

    SpringBoot?項(xiàng)目中創(chuàng)建線(xiàn)程池

    這篇文章主要介紹了SpringBoot?項(xiàng)目中創(chuàng)建線(xiàn)程池,文章基于Spring?Boot項(xiàng)目創(chuàng)建線(xiàn)程池ThreadPoolExecutor,需要的小伙伴可以參考一下
    2022-04-04
  • 如何動(dòng)態(tài)改變Retrofit的base url和rest版本詳解

    如何動(dòng)態(tài)改變Retrofit的base url和rest版本詳解

    這篇文章主要給大家介紹了關(guān)于如何動(dòng)態(tài)改變Retrofit的base url和rest版本的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2018-09-09
  • SpringBoot工程啟動(dòng)順序與自定義監(jiān)聽(tīng)超詳細(xì)講解

    SpringBoot工程啟動(dòng)順序與自定義監(jiān)聽(tīng)超詳細(xì)講解

    這篇文章主要介紹了SpringBoot工程啟動(dòng)順序與自定義監(jiān)聽(tīng),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧
    2022-11-11
  • Java組件javabean用戶(hù)登錄實(shí)例詳解

    Java組件javabean用戶(hù)登錄實(shí)例詳解

    這篇文章主要為大家詳細(xì)介紹了Java組件javabean用戶(hù)登錄實(shí)例,內(nèi)容有用戶(hù)登錄,注冊(cè)和退出等,感興趣的小伙伴們可以參考一下
    2016-05-05
  • 詳解Spring Boot配置排序依賴(lài)技巧

    詳解Spring Boot配置排序依賴(lài)技巧

    本篇文章主要介紹了Spring Boot配置排序依賴(lài)技巧,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-11-11
  • springboot如何整合elasticsearch

    springboot如何整合elasticsearch

    這篇文章主要介紹了springboot如何整合elasticsearch問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-04-04

最新評(píng)論