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

關(guān)于Spring源碼是如何解決Bean的循環(huán)依賴(lài)

 更新時(shí)間:2021年12月14日 10:54:13   作者:弦上的夢(mèng)  
這篇文章主要介紹了關(guān)于Spring源碼是如何解決Bean的循環(huán)依賴(lài),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

首先需要明白一點(diǎn),只有scop為(singleton)單例類(lèi)型的Bean,spring才支持循環(huán)依賴(lài)。

scope為(prototype)原型類(lèi)型的Bean是不支持的,它每次調(diào)用都會(huì)創(chuàng)建一個(gè)新的實(shí)例,spring 在實(shí)例化bean的時(shí)候會(huì)先實(shí)例化bean的各種屬性依賴(lài),如果TestA TestB是原型類(lèi)型且相互依賴(lài)則會(huì)出現(xiàn)new TestA 的時(shí)候,先new TestB,然后new TestB的時(shí)候又去new TestA會(huì)出現(xiàn)無(wú)限套娃的情況。

兩個(gè)單例testA testB 互相依賴(lài)的實(shí)例化過(guò)程

Spring容器創(chuàng)建單例“testA”bean

首先根據(jù)無(wú)參構(gòu)造器創(chuàng)建bean,并暴露一個(gè)“ObjectFactory”用于返回一個(gè)提前暴露正在創(chuàng)建中的bean,并將“testA”標(biāo)識(shí)符放到“當(dāng)前創(chuàng)建bean池”,然后進(jìn)行setter注入“testB”。

Spring容器創(chuàng)建單例“testB”bean

首先根據(jù)無(wú)參構(gòu)造器創(chuàng)建bean,并暴露一個(gè)“ObjectFactory”用于返回一個(gè)提前暴露正在創(chuàng)建中的bean,并將“testB”標(biāo)識(shí)符放到“當(dāng)前創(chuàng)建bean池”,然后進(jìn)行setter注入“testA”,此時(shí)由于通過(guò) 暴露"ObjectFactory" 已提前暴露了一個(gè)正在創(chuàng)建中的"testA" bean,所以直接注入,完成testB的創(chuàng)建,注入testA中,再完成testA的創(chuàng)建。

源碼中的實(shí)現(xiàn)方式

首先了解一下創(chuàng)建Bean過(guò)程中最重要的三個(gè)map

以下三個(gè)Map均來(lái)自于 DefaultSingletonBeanRegistry

Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
  • singletonObjects:用于保存BeanName和創(chuàng)建bean實(shí)例之間的關(guān)系,bean name 一> bean instance。
  • singletonFactories:用于保存BeanName和創(chuàng)建bean的工廠(chǎng)之間的關(guān)系,bean name 一>ObjectFactory。
  • earlySingletonObjects:也是保存BeanName和創(chuàng)建bean實(shí)例之間的關(guān)系,與singletonObjects的不同之處在于,當(dāng)一個(gè)單例bean被放到這里面后,那么當(dāng)bean還在創(chuàng)建過(guò)程中,就可以通過(guò)getBean方法獲取到了,其目的是用來(lái)檢測(cè)循環(huán)引用。

總結(jié):后面兩個(gè)Map實(shí)際上就是為了輔助第一個(gè)Map緩存Bean的實(shí)例,完成后數(shù)據(jù)就在后面兩個(gè)Map中清掉了。

測(cè)試代碼:

// 1. 引入依賴(lài),springboot項(xiàng)目只需要這一個(gè)依賴(lài)即可測(cè)試
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
// 2. 兩個(gè)測(cè)試類(lèi)
@Component
public class TestA {
    @Autowired
    private TestB testB;
}
@Component
public class TestB {
    @Autowired
    private TestA testA;
}

注意:下面所有的方法都只是源碼的部分截取,把我認(rèn)為重要的邏輯放在這里的,大家閱讀時(shí),可提前在IDE中打開(kāi)文中提到的幾個(gè)類(lèi),在相應(yīng)方法處,打上斷點(diǎn)可以直接調(diào)試,bean的實(shí)例化過(guò)程就一目了然了。

1. AbstractBeanFactory類(lèi)中g(shù)etBean方法

public <T> T getBean(String name, @Nullable Class<T> requiredType, @Nullable Object... args)
  throws BeansException {
 return doGetBean(name, requiredType, args, false);
}

2. AbstractBeanFactory類(lèi)中doGetBean方法

// 
// 2.1 從緩存中獲取實(shí)例Bean,第一次肯定沒(méi)有,為null
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
 beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}else{
 // Create bean instance.
 if (mbd.isSingleton()) {
  // 2.2 獲取緩存中的實(shí)例
  sharedInstance = getSingleton(beanName, () -> {
   try {
    // 2.3 調(diào)用創(chuàng)建Bean實(shí)例的方法
    return createBean(beanName, mbd, args);
   } 
  });
  beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
 }
}

3. DefaultSingletonBeanRegistry類(lèi)中g(shù)etSingleton方法

2.1調(diào)用的就是這里的3.1

// 3.1
@Override
@Nullable
public Object getSingleton(String beanName) {
 return getSingleton(beanName, true);
}
// 3.2
@Nullable
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();
       this.earlySingletonObjects.put(beanName, singletonObject);
       this.singletonFactories.remove(beanName);
      }
     }
    }
   }
  }
 }
 return singletonObject;
}

3.1和3.2后面會(huì)反復(fù)獲取的,第一次因?yàn)閕sSingletonCurrentlyInCreation(beanName)返回false,所以返回null

4. DefaultSingletonBeanRegistry類(lèi)中g(shù)etSingleton方法

獲取ObjectFactory,2.2就是調(diào)用的這里

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
 synchronized (this.singletonObjects) {
  Object singletonObject = this.singletonObjects.get(beanName);
  if (singletonObject == null) {
   beforeSingletonCreation(beanName);
   boolean newSingleton = false;
   try {
    // 4.0.1
    singletonObject = singletonFactory.getObject();
    newSingleton = true;
   }
   finally {
    afterSingletonCreation(beanName);
   }
   if (newSingleton) {
    // 4.0.2
    addSingleton(beanName, singletonObject);
   }
  }
  return singletonObject;
 }
}
protected void addSingleton(String beanName, Object singletonObject) {
 synchronized (this.singletonObjects) {
  this.singletonObjects.put(beanName, singletonObject);
  this.singletonFactories.remove(beanName);
  this.earlySingletonObjects.remove(beanName);
  this.registeredSingletons.add(beanName);
 }
}

這面重點(diǎn)分析beforeSingletonCreation 、afterSingletonCreation 和 addSingleton這三個(gè)方法

4.1 DefaultSingletonBeanRegistry 中 beforeSingletonCreation方法 和 afterSingletonCreation方法

protected void beforeSingletonCreation(String beanName) {
 if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
  throw new BeanCurrentlyInCreationException(beanName);
 }
}
protected void afterSingletonCreation(String beanName) {
 if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
  throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
 }
}

重點(diǎn):這兩個(gè)方法的目的就是為 singletonsCurrentlyInCreation 這個(gè)set集合添加和刪除當(dāng)前創(chuàng)建的Bean,為后續(xù)處理做鋪墊,addSingleton方法主要是對(duì)Bean緩存map的維護(hù)。

4.2 現(xiàn)在回到第4步的4.0.1的方法中,singletonObject = singletonFactory.getObject();這行代碼就是正真獲取實(shí)例對(duì)象的地方,singletonFactory 是怎么拿到的呢,這就要回到第2步的2.3步驟中,通過(guò)createBean方法返回了ObjectFactory類(lèi)型的singletonFactory,下面看createBean是如何創(chuàng)建Bean的:

protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {  
 try {
  Object beanInstance = doCreateBean(beanName, mbdToUse, args);
  return beanInstance;
 } 
}
// 看過(guò)一些spring源碼的都應(yīng)該明白spring真正做事情的都是以doXXX開(kāi)頭的,這里也不例外
// 相信大家都已經(jīng)明白真正創(chuàng)建Bean是由doCreateBean方法實(shí)現(xiàn)的,下面我們繼續(xù)分析這個(gè)方法
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
 BeanWrapper instanceWrapper = null;
 if (instanceWrapper == null) {
  instanceWrapper = createBeanInstance(beanName, mbd, args);
 }
 Object bean = instanceWrapper.getWrappedInstance();
 boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences
  && isSingletonCurrentlyInCreation(beanName));
 if (earlySingletonExposure) {
  addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
 }
 Object exposedObject = bean;
 try {
  // 4.3 填充Bean的屬性,依賴(lài)bean就是這里初始化的
  populateBean(beanName, mbd, instanceWrapper);
  exposedObject = initializeBean(beanName, exposedObject, mbd);
 }
 if (earlySingletonExposure) {
  // 4.4 再次獲取緩存中的實(shí)例,注意這里可以從兩個(gè)緩存處獲取,第一個(gè)是earlySingletonObjects map,第二個(gè)是singletonFactories map獲取
  Object earlySingletonReference = getSingleton(beanName, false);
  if (earlySingletonReference != null) {
   if (exposedObject == bean) {
    exposedObject = earlySingletonReference;
   }
  }
 }
 return exposedObject;
}
// isSingletonCurrentlyInCreation方法
public boolean isSingletonCurrentlyInCreation(String beanName) {
 return this.singletonsCurrentlyInCreation.contains(beanName);
}
// addSingletonFactory
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
 Assert.notNull(singletonFactory, "Singleton factory must not be null");
 synchronized (this.singletonObjects) {
 if (!this.singletonObjects.containsKey(beanName)) {
  this.singletonFactories.put(beanName, singletonFactory);
  this.earlySingletonObjects.remove(beanName);
  this.registeredSingletons.add(beanName);
 }
}

重要流程梳理

1.doCreateBean中主要分為兩部分

第一部分通過(guò)instanceWrapper得到BeanFactory的實(shí)例,內(nèi)部由反射實(shí)現(xiàn),這里我們不多做分析,變量earlySingletonExposure,它由三部分得到,前面兩個(gè)都很容易理解,第三部分則出現(xiàn)了我們?cè)?.1中做鋪墊的集合 singletonsCurrentlyInCreation。

由于在4.1中已經(jīng)設(shè)置了,所以earlySingletonExposure肯定為true,因此執(zhí)行addSingletonFacatory為singletonFactories map賦值,完成了beanName -> ObjectFactory的映射

2.populateBean方法中

則會(huì)完成對(duì)Bean依賴(lài)屬性的注入,因此代碼走到4.3的時(shí)候,testA的創(chuàng)建就停止了,會(huì)回到第一步去獲取testB,然后又是對(duì)testB的創(chuàng)建,最后會(huì)再次走到4.3,完成testA 和 testB 的ObjectFactory的映射,即將它們放入 singletonFactories map緩存中。

3.創(chuàng)建testB 再次走到4.3的時(shí)候

又會(huì)去初始化testB的依賴(lài) testA,此時(shí)會(huì)再次去第一步獲取,再次走到2.1的時(shí)候,因?yàn)閠estA的ObjectFactory是有值的,所以通過(guò)它能夠獲取到testA 的singletonObject,此時(shí)就把testA 的實(shí)例放入了 earlySingletonObjects中,只不過(guò)此時(shí)的testA實(shí)例是不完整的,還沒(méi)有完成屬性testB依賴(lài)的初始化。

最后返回testA的singletonObject引用,完成testB對(duì)其依賴(lài)testA的初始化,然后再去 4.4 獲取testB的緩存,這里依舊是沒(méi)有的,然后返回到4.0.2處,將testB加入singletonObjects map緩存,并移除testB在singletonFactories中的緩存,這里testB 在 earlySingletonObjects中實(shí)際上是沒(méi)有值的,當(dāng)然有的話(huà)也會(huì)移除,因?yàn)閟ingletonObjects 中已經(jīng)拿到值了,所以另外兩個(gè)輔助map就不用保留數(shù)據(jù)了。

4.上面已經(jīng)完成testB的初始化

并放入singletonObjects,緩存了,繼續(xù)走,就又回到了4.3,繼續(xù)完成對(duì)testA的創(chuàng)建,走到4.4的時(shí)候,繼續(xù)去緩存中獲取testA,因?yàn)橹耙呀?jīng)把testA放入earlySingletonObjects map中了,所以4.4是直接能夠獲取到testA的實(shí)例的。

5.繼續(xù)走,就又來(lái)到了4.0.2

不過(guò)這次是針對(duì)testA的,addSingleton方法中會(huì)把testA的實(shí)例給放入singletonObjects map緩存中,同時(shí)移除singletonFactories 和 earlySingletonObjects map緩存的testA,完成testA和testB的實(shí)例化。

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

相關(guān)文章

  • Java中Scanner類(lèi)基礎(chǔ)使用、可能遇到的問(wèn)題及注意事項(xiàng)

    Java中Scanner類(lèi)基礎(chǔ)使用、可能遇到的問(wèn)題及注意事項(xiàng)

    Scanner類(lèi)是一個(gè)用于Scanner指的是java.util包下的Scanner類(lèi),可以接收控制臺(tái)輸入的數(shù)據(jù),這篇文章主要介紹了Java中Scanner類(lèi)基礎(chǔ)使用、可能遇到的問(wèn)題及注意事項(xiàng)的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2025-04-04
  • java程序員常見(jiàn)的sql錯(cuò)誤

    java程序員常見(jiàn)的sql錯(cuò)誤

    當(dāng)Java程序員在SQL中要寫(xiě)個(gè)查詢(xún)語(yǔ)句是很簡(jiǎn)單的。但在Java里類(lèi)似的語(yǔ)句卻不容易,因?yàn)槌绦騿T不僅要反復(fù)考慮編程范式,而且也要考慮算法的問(wèn)題。下面我們來(lái)看看這幾個(gè)常見(jiàn)的錯(cuò)誤吧
    2019-06-06
  • 使用JMeter進(jìn)行接口高并發(fā)測(cè)試的實(shí)現(xiàn)

    使用JMeter進(jìn)行接口高并發(fā)測(cè)試的實(shí)現(xiàn)

    本文主要介紹了使用JMeter進(jìn)行接口高并發(fā)測(cè)試的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-04-04
  • SpringBoot如何打印mybatis的執(zhí)行sql問(wèn)題

    SpringBoot如何打印mybatis的執(zhí)行sql問(wèn)題

    這篇文章主要介紹了SpringBoot如何打印mybatis的執(zhí)行sql問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • 詳解SpringSecurity處理會(huì)話(huà)管理和注銷(xiāo)功能

    詳解SpringSecurity處理會(huì)話(huà)管理和注銷(xiāo)功能

    本文介紹了詳解SpringSecurity處理會(huì)話(huà)管理和注銷(xiāo)功能,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2025-01-01
  • Java NIO實(shí)戰(zhàn)之聊天室功能詳解

    Java NIO實(shí)戰(zhàn)之聊天室功能詳解

    這篇文章主要介紹了Java NIO實(shí)戰(zhàn)之聊天室功能,結(jié)合實(shí)例形式詳細(xì)分析了java NIO聊天室具體的服務(wù)端、客戶(hù)端相關(guān)實(shí)現(xiàn)方法與操作注意事項(xiàng),需要的朋友可以參考下
    2019-11-11
  • springboot配置請(qǐng)求超時(shí)時(shí)間(Http會(huì)話(huà)和接口訪(fǎng)問(wèn))

    springboot配置請(qǐng)求超時(shí)時(shí)間(Http會(huì)話(huà)和接口訪(fǎng)問(wèn))

    本文主要介紹了springboot配置請(qǐng)求超時(shí)時(shí)間,包含Http會(huì)話(huà)和接口訪(fǎng)問(wèn)兩種,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2024-07-07
  • SpringBoot整合模板引擎過(guò)程代碼實(shí)例

    SpringBoot整合模板引擎過(guò)程代碼實(shí)例

    這篇文章主要介紹了SpringBoot整合模板引擎過(guò)程代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-07-07
  • Java正則表達(dá)式之split()方法實(shí)例詳解

    Java正則表達(dá)式之split()方法實(shí)例詳解

    這篇文章主要介紹了Java正則表達(dá)式之split()方法,結(jié)合實(shí)例形式較為詳細(xì)的分析了split方法的功能、使用方法及相關(guān)注意事項(xiàng),需要的朋友可以參考下
    2017-03-03
  • java8傳函數(shù)方法圖文詳解

    java8傳函數(shù)方法圖文詳解

    在本篇文章中小編給大家整理了關(guān)于java8傳函數(shù)方法和相關(guān)知識(shí)點(diǎn),需要的朋友們學(xué)習(xí)下。
    2019-04-04

最新評(píng)論

大安市| 五常市| 山东省| 威海市| 伊通| 青岛市| 荆门市| 改则县| 白河县| 会理县| 东兴市| 塔城市| 名山县| 武冈市| 石门县| 崇左市| 福贡县| 威海市| 遵化市| 大荔县| 宜阳县| 洪洞县| 米脂县| 娄底市| 民权县| 天长市| 英超| 通山县| 新宾| 临清市| 东乌珠穆沁旗| 金门县| 迁安市| 开封县| 长武县| 黑河市| 大竹县| 和硕县| 鹤山市| 托克托县| 宜良县|