最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

spring循環(huán)依賴策略解析

 更新時(shí)間:2020年09月18日 08:35:51   作者:atheva  
這篇文章主要為大家詳細(xì)介紹了spring循環(huán)依賴策略,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下

循環(huán)依賴

所謂循環(huán)依賴就是多個(gè)Bean之間依賴關(guān)系形成一個(gè)閉環(huán),例如A->B->C->...->A 這種情況,當(dāng)然,最簡(jiǎn)單的循環(huán)依賴就是2個(gè)Bean之間互相依賴:A->B(A依賴B), B->A(B依賴A) 。在Spring中,如果A->B,那么在創(chuàng)建A的過程中會(huì)去創(chuàng)建B,在創(chuàng)建B(或B的依賴)的過程中又發(fā)現(xiàn)B->A,這個(gè)時(shí)候就出現(xiàn)了循環(huán)依賴的現(xiàn)象。

循環(huán)依賴的解決

spring中的循環(huán)依賴只有當(dāng)

1.Bean是單例,
2.通過屬性注入的情況

這兩個(gè)條件滿足的情況下是沒問題的。但是如果是通過構(gòu)造器依賴,或者不是單例模式的情況下循環(huán)依賴就會(huì)拋出異常BeanCurrentlyInCreationException。下面從代碼層面上解析一下為什么。

Prototype的循環(huán)依賴問題

為什么最先介紹Prototype的循環(huán)依賴呢,因?yàn)榭梢皂槺憬榻B在Spring中創(chuàng)建Bean的流程核心流程:在AbstractFoctory的doGetBean的方法。這個(gè)方法很長(zhǎng),這里只寫出核心邏輯,并在注解上注明了個(gè)人理解:

protected <T> T doGetBean(
  final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
  throws BeansException {
 
 final String beanName = transformedBeanName(name);
 Object bean;
 
 //嘗試獲取單例對(duì)象,因?yàn)閟pring大部分的bean都是單例的,所以這里先嘗試能否獲取。
 registered singletons.
 Object sharedInstance = getSingleton(beanName);
 //單例存在的情況下,那么beanName返回的肯定是單例類,但是這里還需要判斷是不是FactoryBean
 if (sharedInstance != null && args == null) {
  ...
  //FactoryBean應(yīng)該返回getObject()對(duì)象
  bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
 }
 
 else {
  //走到這里,有可能beanName是單例模式,但之前并沒有實(shí)例化,或者是Prototype類型。
  //首先判斷不是循環(huán)依賴,這里的循環(huán)依賴指的是Prototype類型
  if (isPrototypeCurrentlyInCreation(beanName)) {
   throw new BeanCurrentlyInCreationException(beanName);
  }
 
 
  try {
   final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
   // 如果是單例,則創(chuàng)建單例模式
   if (mbd.isSingleton()) {
    // !!!這里是解決單例循環(huán)依賴的關(guān)鍵,后面再分析
    sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
     @Override
     public Object getObject() throws BeansException {
      try {
       return createBean(beanName, mbd, args);
      }
      catch (BeansException ex) {
       throw ex;
      }
     }
    });
    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
   }
 
   else if (mbd.isPrototype()) {
    // 原型模式,則創(chuàng)建一個(gè)新對(duì)象.
    Object prototypeInstance = null;
    try {
     /*這里是Prototype循環(huán)依賴的問題,會(huì)記錄在map中beanName,
     如果在解決當(dāng)前Bean的依賴過程中還依賴當(dāng)前Bean,
     則說明了出現(xiàn)了循環(huán)依賴
     */
       beforePrototypeCreation(beanName);
       prototypeInstance = createBean(beanName, mbd, args);
    }
    finally {
     //對(duì)應(yīng)beforePrototypeCreation(),從map中移除
       afterPrototypeCreation(beanName);
    }
    bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
   }
   ...
  }
 }
 
 ...
 return (T) bean;
}

可以看出,該流程中就考慮了Prototype的循環(huán)依賴的問題,只要在創(chuàng)建Prototype的Bean中出現(xiàn)循環(huán)依賴那么就拋出異常。但是在singleton的情況下,則通過另外的方式來解決。

Singleton的循環(huán)依賴之構(gòu)造注入

在上面介紹中,出現(xiàn)了一個(gè)很關(guān)鍵的地方:

sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
 @Override
 public Object getObject() throws BeansException {
  try {
   return createBean(beanName, mbd, args);
  }
  catch (BeansException ex) {
   throw ex;
  }
 }
});

這個(gè)getSingleton涉及到了ObjectFactory這個(gè)接口類,這個(gè)接口的功能和FactoryBean類似,但是主要是用來解決循環(huán)依賴的。在初始化過程同決定返回的Singleton對(duì)象是。關(guān)于單例的對(duì)象的創(chuàng)建,又要介紹一下DefaultSingletonBeanRegistry這個(gè)類,這個(gè)類主要用來幫助創(chuàng)建單例模式,其中主要的屬性:

/** 緩存創(chuàng)建的單例對(duì)象: bean名字 --> bean對(duì)象 */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);

/** 緩存單例的factory,就是ObjectFactory這個(gè)東西,: bean name --> ObjectFactory */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);

/** 也是緩存創(chuàng)建的單例對(duì)象,功能和singletonObjects不一樣,
在bean構(gòu)造成功之后,屬性初始化之前會(huì)把對(duì)象放入到這里,
主要是用于解決屬性注入的循環(huán)引用: bean name --> bean instance 
*/
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);

/** 記錄在創(chuàng)建單例對(duì)象中循環(huán)依賴的問題,還記得Prototype中又記錄創(chuàng)建過程中依賴的map嗎?
在Prototype中只要出現(xiàn)了循環(huán)依賴就拋出異常,而在單例中會(huì)嘗試解決 */
private final Set<String> singletonsCurrentlyInCreation =
  Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>(16));

現(xiàn)在回過來看getSingleton(beanName, new ObjectFactory<Object>()這個(gè)方法的實(shí)現(xiàn)。

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {

 synchronized (this.singletonObjects) {
  //嘗試在singletonObjects中獲取
  Object singletonObject = this.singletonObjects.get(beanName);
  if (singletonObject == null) {
   //不存在則創(chuàng)建
   //把當(dāng)前beanName加入到singletonsCurrentlyInCreation中
   beforeSingletonCreation(beanName);
   try {
    
    singletonObject = singletonFactory.getObject();
   }
   ...
   finally {
    ...
    //從singletonsCurrentlyInCreation中刪除beanName
    afterSingletonCreation(beanName);
   }
  }
  return (singletonObject != NULL_OBJECT ? singletonObject : null);
 }
}

這段邏輯是不是和Prototype中解決循環(huán)類似,這里其實(shí)就是調(diào)用了ObjectFactory的getObject()獲取對(duì)象,回過頭去看前面代碼,ObjectFactory的getObject()方法實(shí)際調(diào)用的是createBean(beanName, mbd, args)。說到createBean(beanName, mbd, args)又不得不說AbstractAutowireCapableBeanFactory這個(gè)類,主要功能就是完成依賴注入的Bean的創(chuàng)建,這個(gè)類的createBean方法代碼如下,注意注解說明:

@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException {
 ...
 Object beanInstance = doCreateBean(beanName, mbdToUse, args);
 ...
}

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
   throws BeanCreationException {

 // 實(shí)例化bean
 BeanWrapper instanceWrapper = null;
 if (mbd.isSingleton()) {
  instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
 }
 if (instanceWrapper == null) {
  //如果沒實(shí)例化則創(chuàng)建新的BeanWrapper
  //如果是通過構(gòu)造器注入,這里是一個(gè)關(guān)鍵點(diǎn)
  /*
  因?yàn)樵贏初始化的時(shí)候發(fā)現(xiàn)構(gòu)造函數(shù)依賴B,就會(huì)去實(shí)例化B,
  然后B也會(huì)運(yùn)行到這段邏輯,構(gòu)造函數(shù)中發(fā)現(xiàn)依賴A,
  這個(gè)時(shí)候就會(huì)拋出循環(huán)依賴的異常
  */
    instanceWrapper = createBeanInstance(beanName, mbd, args);
 }
 

 //如果當(dāng)前是單例,并且allowCircularReferences為true(默認(rèn)就是true,除非我們不希望Spring幫我們解決)
 boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
   isSingletonCurrentlyInCreation(beanName));
 if (earlySingletonExposure) {
  /*
  ?。?!這里很重要,把構(gòu)造成功,但屬性還沒注入的
  的bean加到singletonFactory中,這樣再解決A的依賴
  過程中如果依賴A,就把這個(gè)半成品返回回去。
  */
  addSingletonFactory(beanName, new ObjectFactory<Object>() {
   @Override
   public Object getObject() throws BeansException {
    return getEarlyBeanReference(beanName, mbd, bean);
   }
  });
 }

 
 Object exposedObject = bean;
 try {
  //自動(dòng)注入屬性
  populateBean(beanName, mbd, instanceWrapper);
  if (exposedObject != null) {
   exposedObject = initializeBean(beanName, exposedObject, mbd);
  }
 }
 ...

 return exposedObject;
}

注解已經(jīng)注明了我的理解。就不再贅述

總結(jié)

上面代碼是我一邊debug一個(gè)寫下的,現(xiàn)在寫完了,根據(jù)自己的理解總結(jié)一下。

相關(guān)類說明

AbstractBeanFactory,這個(gè)類中包含了Bean創(chuàng)建的主要流程,在doGetBean這個(gè)方法中包含了對(duì)Prototype循環(huán)依賴處理。邏輯很簡(jiǎn)單,出現(xiàn)了循環(huán)依賴則直接拋出異常

DefaultSingletonBeanRegister 用于管理Singleton的對(duì)象的創(chuàng)建,以及解決循環(huán)依賴的問題,其中解決循環(huán)依賴的關(guān)鍵屬性就是了earlySingletonObjects,他會(huì)在構(gòu)造Singleton對(duì)象過程中暫時(shí)緩存構(gòu)造成功,但屬性還未注入的對(duì)象,這樣就可以解決循環(huán)依賴的問題。

AbstractAutowireCapableBeanFactory,自動(dòng)注入的相關(guān)邏輯,包自動(dòng)注入的對(duì)象的創(chuàng)建、初始化和注入。但如果在調(diào)用構(gòu)造函數(shù)中發(fā)現(xiàn)了循環(huán)依賴,則拋出異常

ObjectFactory,這個(gè)接口功能和FactoryBean類似,但是為了解決循環(huán)依賴,他決定了在獲取的getSingleton()是一個(gè)完成品還是一個(gè)半成品。

思考

如果A--構(gòu)造依賴->B,B--屬性依賴-->A,例如:

@Component
public class BeanA {
 private BeanB beanB;

 @Autowired
 public BeanA(BeanB beanB) {
  this.beanB = beanB;
 }
}

@Component
public class BeanB {
 @Autowired
 private BeanA beanA;
}

這種情況會(huì)異常嗎?提示:都有可能

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java File類常用方法與文件過濾器詳解

    Java File類常用方法與文件過濾器詳解

    Java File類以抽象的方式代表文件名和目錄路徑名。該類主要用于文件和目錄的創(chuàng)建、文件的查找和文件的刪除等。File對(duì)象代表磁盤中實(shí)際存在的文件和目錄。本篇文章我們來講解File類的常用方法與文件過濾器
    2022-04-04
  • Java窗口精細(xì)全方位講解

    Java窗口精細(xì)全方位講解

    這篇文章呢,將會(huì)系統(tǒng)的精細(xì)的教會(huì)鐵鐵們?nèi)绾巫约簩懸粋€(gè)完整的窗口;看完之后窗口穩(wěn)拿下!!!所以呢由于詳細(xì),知識(shí)點(diǎn)多,可能有點(diǎn)長(zhǎng),鐵鐵們慢慢仔細(xì)閱讀吧;文章寫的還是一如既往快樂的,哈哈哈
    2021-08-08
  • 接口簽名怎么用Java實(shí)現(xiàn)

    接口簽名怎么用Java實(shí)現(xiàn)

    今天帶大家學(xué)習(xí)java的相關(guān)知識(shí),文章圍繞怎么用Java實(shí)現(xiàn)接口簽名展開,文中有非常詳細(xì)的代碼示例及介紹,需要的朋友可以參考下
    2021-06-06
  • 使用Spring處理x-www-form-urlencoded方式

    使用Spring處理x-www-form-urlencoded方式

    這篇文章主要介紹了使用Spring處理x-www-form-urlencoded方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • SpringBoot使用MyBatis-Plus解決Invalid?bound?statement異常

    SpringBoot使用MyBatis-Plus解決Invalid?bound?statement異常

    這篇文章主要介紹了SpringBoot使用MyBatis-Plus解決Invalid?bound?statement異常,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-09-09
  • SpringCloud?服務(wù)注冊(cè)中的nacos實(shí)現(xiàn)過程

    SpringCloud?服務(wù)注冊(cè)中的nacos實(shí)現(xiàn)過程

    這篇文章主要介紹了SpringCloud?服務(wù)注冊(cè)之nacos實(shí)現(xiàn)過程,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-03-03
  • struts2實(shí)現(xiàn)多文件上傳的示例代碼

    struts2實(shí)現(xiàn)多文件上傳的示例代碼

    本篇文章主要介紹了struts2實(shí)現(xiàn)多文件上傳的示例代碼,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-03-03
  • MyBatis-Plus里面的增刪改查詳解(化繁為簡(jiǎn))

    MyBatis-Plus里面的增刪改查詳解(化繁為簡(jiǎn))

    這篇文章主要給大家介紹了關(guān)于MyBatis-Plus里面的增刪改查的相關(guān)資料,Mybatis-Plus是一個(gè)基于Mybatis的增強(qiáng)工具,可以簡(jiǎn)化Mybatis的開發(fā),提高開發(fā)效率,需要的朋友可以參考下
    2023-07-07
  • Java設(shè)計(jì)模式模板方法模式(Template)用法解析

    Java設(shè)計(jì)模式模板方法模式(Template)用法解析

    這篇文章主要介紹了Java設(shè)計(jì)模式模板方法模式(Template)用法解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-11-11
  • Spring Boot整合mybatis并自動(dòng)生成mapper和實(shí)體實(shí)例解析

    Spring Boot整合mybatis并自動(dòng)生成mapper和實(shí)體實(shí)例解析

    本文是小編給大家總結(jié)的關(guān)于Spring Boot整合mybatis并自動(dòng)生成mapper和實(shí)體的內(nèi)容,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2018-05-05

最新評(píng)論

札达县| 聂拉木县| 德令哈市| 昌乐县| 潞西市| 博客| 昌黎县| 郑州市| 枣强县| 库车县| 泰和县| 长治县| 吉林市| 容城县| 松原市| 开平市| 紫阳县| 沂水县| 湄潭县| 桐柏县| 博爱县| 额尔古纳市| 奉贤区| 建水县| 娱乐| 永福县| 金昌市| 莱阳市| 旺苍县| 鄂温| 竹北市| 武鸣县| 获嘉县| 辽中县| 朝阳区| 桐庐县| 无锡市| 元江| 宁陕县| 西乌| 中方县|