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

Java?Spring?循環(huán)依賴解析

 更新時(shí)間:2022年02月09日 11:18:38   作者:心城以北  
這篇文章主要介紹了Java?Spring?循環(huán)依賴解析,Spring?現(xiàn)在其實(shí)是我們?Java?程序開發(fā)離不開的基礎(chǔ)框架,個(gè)人覺得除了?JDK?我們用得最多的?Java?中間件就是?Spring?,今天我們一起來學(xué)習(xí)一下?Spring?的循環(huán)依賴。下面詳細(xì)內(nèi)容需要的小伙伴可以參考一下

1、常見問題

  • 你解釋一下 spring 中的三級(jí)緩存?
  • 三級(jí)緩存分別是什么?三個(gè) Map 有什么異同?
  • 什么是循環(huán)依賴?請(qǐng)談?wù)??你看過 spring 的源碼嗎?一般我們說的是 spring  容器是什么?
  • 多例的情況下,循環(huán)依賴問題為什么無法解決?

2、什么是循環(huán)依賴?

多個(gè) bean 之間相互依賴,形成閉環(huán)。比如:A 依賴于 B, B 依賴于 C , C 依賴于 A

示例代碼:

public class T1 {

? class A {
? ? B b;
? }
? class B {
? ? C c;
? }
? class C {
? ? A a;?
? }
}

比如:A 依賴于 B, B 依賴于 C , C 依賴于 A
通常來說,如果問 Spring 容器內(nèi)部如何解決循環(huán)依賴,一定是指默認(rèn)的單例 Bean 中, 屬性相互引用的場景。

@Component
public class A {
? ? @Autowired
?? ?private B b;
}

@Component
public class B {
? ? @Autowired
?? ?private C c;
}
?
@Component
public class C {
? ? @Autowired
?? ?private A a;
}

3、循環(huán)依賴說明

官方說明:

參考官方說

構(gòu)造方法注入:不支持循環(huán)依賴。
我們 AB 循環(huán)依賴問題。只要 A 的方式是 setter 且 singleton
就不會(huì)有循環(huán)依賴問題。

4、BeanCurrentlyInCreationException

循環(huán)依賴異常的定義如下所示,如果出現(xiàn)循環(huán)依賴,我們?cè)趩?dòng)/運(yùn)行過程中會(huì)報(bào)這個(gè)錯(cuò)誤。

5、依賴注入的兩種方式

方式一:構(gòu)造器方式注入依賴

@Component
public class ServiceA{

? ? private ServiceB serviceB;
? ? ? ??
? ? public ServiceA(ServiceB serverB) {
? ? ? ?this.serivceB = serviceB;
? ? } ? ??
}

@Component
public class ServiceB{

? ? private ServiceA serviceA;
? ? ? ??
? ? public ServiceB(ServiceA serviceA) {
? ? ? ?this.serviceA = serviceA;
? ? } ? ??
}

構(gòu)造器循環(huán)依賴是無法解決的,你想讓構(gòu)造器注入支持循環(huán)依賴,是不可能的。

方式二:以 set 方式注入依賴

@Component
public class ServiceA{

? ? private ServiceB serviceB;
? ? ? ??
? ? public setServiceB(ServiceB serverB) {
? ? ? ?this.serivceB = serviceB;
? ? } ? ??
}

@Component
public class ServiceB{

? ? private ServiceA serviceA;
? ? ? ??
? ? public setServiceB(ServiceA serviceA) {
? ? ? ?this.serviceA = serviceA;
? ? } ? ??
}

案例演示(基于 Spring 容器的循環(huán)依賴)

普通的 Java 基礎(chǔ)編碼A  類、B 類

@Data
public class A{
? private B b;
??
? public A() {
? ? ?System.out.println("----- A create success");
? }
}

@Data
public class B{
? private a a;

? public B() {
? ? ?System.out.println("------ B create success");
? } ? ?
}

循環(huán)依賴解決

A a = new A();
B b = new B();

a.setB(b);
b.setA(a);

基于 Spring 容器的循環(huán)依賴

  • 默認(rèn)的單例(singleton)的場景是支持循環(huán)依賴的,不報(bào)錯(cuò)
  • 原型(prototype)的場景是不支持循環(huán)依賴的, 會(huì)報(bào)錯(cuò)

代碼演示:

循環(huán)依賴代碼:

@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class A {

? ? @Autowired
? ? private B b;
}

@Component
public class B {

? ? @Autowired
? ? private A a;
}

默認(rèn)單例,修改為原型

// 增加注解
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)

一段測(cè)試程序

class BTest {


? ? @Configuration
? ? @Import({A.class, B.class})
? ? public static class TestConfig {

? ? }

? ? @Test
? ? public void currentlyincreation() {
? ? ? ? AnnotationConfigApplicationContext applicationContext =
? ? ? ? ? ? ? ? new AnnotationConfigApplicationContext(TestConfig.class);
? ? ? ? // 這里要獲取 bean 一下,如果不去主動(dòng)獲取,可能是由于惰性加載沒有執(zhí)行,不會(huì)報(bào)錯(cuò)
? ? ? ? A a = applicationContext.getBean(A.class);
? ? ? ? System.out.println(a);
? ? }
}

循環(huán)依賴異常

6、Spring 三級(jí)緩存介紹和循環(huán)依賴解決過程

三級(jí)緩存介紹

核心類: DefaultSingletonBeanRegistry

第一級(jí)緩存:(也叫單例池)singletonObjects: 存放已經(jīng)經(jīng)歷完整生命周期的 Bean 對(duì)象
第二級(jí)緩存:earlySingletonObjects: 存放早起暴露出來的 Bean 對(duì)象, Bean 的生命周期未結(jié)束(屬性還未填充完成)
第三級(jí)緩存:Map<String, ObjectFactory<?>> singletonFactories可以存放 Bean 工廠。
只有單例 Bean 會(huì)通過三級(jí)緩存提前暴露出來解決循環(huán)依賴問題,而非單例的 bean, 每次從容器獲取都是新的對(duì)象,都會(huì)重新創(chuàng)建,所以非單例的 bean 是沒有緩存的,不會(huì)放到三級(jí)緩存中。

實(shí)例化/初始化定義

實(shí)例化/初始化

1、實(shí)例化:內(nèi)存中申請(qǐng)一塊內(nèi)存空間;(比如:租賃好好房子,自己的家具沙發(fā),床還沒有搬進(jìn)去。)
2、初始化屬性填充:完成各種屬性的賦值;(比如:裝修,家電家具進(jìn)場)

三級(jí)緩存使用過程

3 個(gè) map 的四大方法,總體相關(guān)對(duì)象

第一層:(也叫單例池)singletonObjects: 存放已經(jīng)初始化好的 Bean。
第二層:earlySingletonObjects: 存放的是實(shí)例化的了, 但是未初始化的 Bean。
第三層:Map<String, ObjectFactory<?>> singletonFactories
存放的是 FactroyBean。假如 A 類實(shí)現(xiàn)了 FactoryBean, 那么依賴注入的時(shí)候不是 A 類,而是 A類的 FAC天FactoryBean。

/**
?* 單例對(duì)象的緩存:bean 名稱--Bean 實(shí)例, 即:所有的單例池
?* 表示經(jīng)歷了完整生命周期的 Bean 對(duì)象
?* <b>第一級(jí)緩存</b>
?*/
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

/**
?* 單例工廠的高速緩存:bean 名稱--ObjectFacotry
?* 表示存放生成的 bean 工廠
?* <b>第三級(jí)緩存</b>
?*/
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

/**
?* 早起的單例對(duì)象的高速緩存:bean 名稱--Bean 實(shí)例
?* 表示 Bean 的生命周期還沒有走完(Bean 的屬性還未填充)就把這個(gè) Bean 存入該緩存中
?* 也就是實(shí)例化的 bean 放入了該緩存中
?* <b>第二級(jí)緩存</b>
?*/
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);

A/B 兩對(duì)象在三級(jí)緩沖的遷移說明

1、創(chuàng)建 A 過程中需要 B, 于是 A 將自己放入到三級(jí)緩存里面,去實(shí)例化 B
2、B 實(shí)例化的時(shí)候發(fā)現(xiàn)需要 A ,于是 B 先查一級(jí)緩存,沒有,再查二級(jí)緩存,還是沒有再查三級(jí)緩存,找到了 A 然后把三級(jí)緩存里面的這個(gè) A 放入到二級(jí)緩存里面,并且刪除三級(jí)緩存里面的 A。
3、 B 順利初始化完畢后,將自己放入到一級(jí)緩存里面(此時(shí) B 里面的 A 依然是創(chuàng)建中狀態(tài))后來接著創(chuàng)建 A, 此時(shí) B 已經(jīng)創(chuàng)建結(jié)束,直接從一級(jí)緩存里面拿到 B,然后完成創(chuàng)建,并且將 A 自己放到一級(jí)緩存中

ObjectFactory 接口

@FunctionalInterface
public interface ObjectFactory<T> {

? ? /**
?? ? * Return an instance (possibly shared or independent)
?? ? * of the object managed by this factory.
?? ? * @return the resulting instance
?? ? * @throws BeansException in case of creation errors
?? ? */
? ? T getObject() throws BeansException;

}

DEBUG 斷點(diǎn)調(diào)試

1、Bean 創(chuàng)建

2、放入三級(jí)緩存

3、屬性填充(就是做屬性注入)

循環(huán)依賴解決

整理流程梳理:

1、調(diào)用 doGetBean() 方法 , 想要獲取 beanA , 于是調(diào)用 getSingletion() 方法從緩存中查詢 beanA
2、在 getSingletion() 方法中,從一級(jí)緩存中查找,沒有,返回 null
3、doGetBean() 犯法中獲取到 beanA 為 null, 于是就走對(duì)應(yīng)的處理邏輯,調(diào)用 getSIngletion() 的重載方法(參數(shù)為 ObjecFactory  的)
4、在 getSingletion() 方法中,先將 beanB_name 添加到一個(gè)集合中,用于標(biāo)記該 bean 正在創(chuàng)建中,然后回調(diào)匿名內(nèi)部類的 createBean  方法
5、進(jìn)入 AbstractAutowireCapableBenaFactory#doCreateBean , 先反射調(diào)用構(gòu)造器創(chuàng)建出 beanA 的實(shí)例,然后判斷,是否為單例、是否允許提前暴露引用(對(duì)于單例一般為 true)、是否正在創(chuàng)建中(即是在第四步的集合中)。判斷為 true 則將 beanA 添加到【三級(jí)緩存】中。
6、對(duì) beanA 進(jìn)行屬性填充,此時(shí)檢測(cè)到 beanA 依賴 beanB , 于是開始查找 beanB
7、調(diào)用 doGetBean 方法,和上面的 beanA 過程一樣,到緩存中查找 beanB, 沒有則創(chuàng)建,然后給 beanB 填充屬性。
8、此時(shí) beanB  依賴于 beanA , 調(diào)用 getSingleton() 獲取 beanA , 依次從一級(jí)、二級(jí)、三級(jí)緩存中找,此時(shí)三級(jí)緩存中獲取到 beanA 的創(chuàng)建工廠,通過創(chuàng)建工廠獲取到 singletonObject, 此時(shí)這個(gè) singletonObject 指向的就是上面的 doCreateBean() 方法實(shí)例化的 beanA 。
9、這樣 beanB 就獲取到了 beanA 的依賴,于是 beanB 順利完成實(shí)例話,并將 beanA 從三級(jí)緩存移動(dòng)到二級(jí)緩存中。
10、隨后 beanA  繼續(xù)他的屬性填充工作,此時(shí)也獲取到了beanB , beanA 也隨之完成了創(chuàng)建,回調(diào) getSingleton() 方法中繼續(xù)向下執(zhí)行,將 beanA 從二級(jí)緩存中移動(dòng)到一級(jí)緩存中。

7、Spring 循環(huán)依賴總結(jié)

Spring 創(chuàng)建 Bean 主要分為兩個(gè)步驟,創(chuàng)建原始 Bean 對(duì)象,接著去填充屬性和初始化。
每次創(chuàng)建 Bean 之前,我們都會(huì)從緩存中查一下有沒有該 bean , 因?yàn)槭菃卫?只能有一個(gè)
當(dāng)我們創(chuàng)建 beanA  的原始對(duì)象之后,并且把它放入到三級(jí)緩存中,接下來就該填充屬性了,這個(gè)時(shí)候發(fā)現(xiàn)依賴了 beanB , 就直接去創(chuàng)建 beanB, 同樣的流程,創(chuàng)建完 beanB 統(tǒng)籌國內(nèi)屬性時(shí)又發(fā)現(xiàn)了它依賴了 beanA  又是同樣的流程。

不同的是:

這個(gè)時(shí)候可以在三級(jí)緩存只能夠查詢到剛才放進(jìn)去的原始對(duì)象 beanA , 所以不需要繼續(xù)創(chuàng)建,用它注入 beanB , 完成 beanB 的創(chuàng)建
既然 beanB 創(chuàng)建好了,所以 beanA 就可以彎沉屬性填充的步驟了,接下來執(zhí)行剩下的邏輯完,完成閉環(huán)

@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
?? ?// 從 singletonObject 獲取實(shí)例,singletonObject 中的實(shí)例都是準(zhǔn)備好的 bean 實(shí)例
? ? Object singletonObject = this.singletonObjects.get(beanName);
? ? // isSingletonCurrentlyInCreation() 判斷當(dāng)前單例 bean 是否正在創(chuàng)建中
? ? 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) {
?? ? ? ? ? ??? ??? ?// 一級(jí)緩存中沒有就去二級(jí)緩存中找?? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? singletonObject = this.earlySingletonObjects.get(beanName);
? ? ? ? ? ? ? ? ? ? if (singletonObject == null) {
? ? ? ? ? ? ? ? ? ? ? ? // 二級(jí)緩存也沒有,就去三級(jí)緩存查找
? ? ? ? ? ? ? ? ? ? ? ? ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
? ? ? ? ? ? ? ? ? ? ? ? if (singletonFactory != null) {
? ? ? ? ? ? ? ? ? ? ? ? ? ? // 三級(jí)緩存存在的化,就把它移動(dòng)到二級(jí)緩存
? ? ? ? ? ? ? ? ? ? ? ? ? ? singletonObject = singletonFactory.getObject();
? ? ? ? ? ? ? ? ? ? ? ? ? ? this.earlySingletonObjects.put(beanName, singletonObject);
? ? ? ? ? ? ? ? ? ? ? ? ? ? this.singletonFactories.remove(beanName);
? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? }
? ? return singletonObject;
}

Spring 解決循環(huán)依賴靠的是 Bean 的“中間態(tài)” 這個(gè)概念。而這個(gè)中間態(tài)指的是 已經(jīng)實(shí)例化還沒有初始化的狀態(tài) ---> 半成品,實(shí)例話的過程有是通過構(gòu)造器創(chuàng)建的,如果 A 還沒有創(chuàng)建好出來怎么可能提前曝光,所以構(gòu)造器的循環(huán)依賴無法解決。

Spring 為了解決單例的循環(huán)依賴問題,使用了三級(jí)緩存:

  • 其中一級(jí)緩存為單例池(singletonObjects)
  • 二級(jí)緩存為提前曝光對(duì)象(earlySingletonObjects)
  • 三級(jí)緩存為提前曝光對(duì)象工廠(singletonFactories)

假設(shè) A,B 循環(huán)引用,實(shí)例化 A 的時(shí)候就將其放入三級(jí)緩存中,接著填充屬性的時(shí)候,發(fā)現(xiàn)依賴了 B ,同樣的流程也是實(shí)例化后放入三級(jí)緩存,接著去填充屬性時(shí)發(fā)現(xiàn)自己依賴 A,這個(gè)時(shí)候從緩存中查找到早起暴露的 A,沒有 AOP 代理的化,直接將 A 原始對(duì)象注入 B,完成 B的初始化后,進(jìn)行屬性填充和初始化,這個(gè)時(shí)候 B完成后,就去完成剩下 A的步驟,如果有 AOP 代理,就會(huì)進(jìn)行 AOP 處理獲取代理后的  A,注入 B, 走剩下的流程。

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

相關(guān)文章

  • 詳解Java Web如何限制訪問的IP的兩種方法

    詳解Java Web如何限制訪問的IP的兩種方法

    這篇文章主要介紹了詳解Java Web如何限制訪問的IP的兩種方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-10-10
  • Java類加載策略之雙親委派機(jī)制全面分析講解

    Java類加載策略之雙親委派機(jī)制全面分析講解

    這篇文章主要介紹了Java雙親委派機(jī)制,雙親委派模型是Java加載類的機(jī)制,采用雙親委派模型的好處是Java類隨著它的類加載器一起具備了一種帶有優(yōu)先級(jí)的層級(jí)關(guān)系,通過這種層級(jí)關(guān)系可以避免類的重復(fù)加載,感興趣的朋友可以參考下
    2023-12-12
  • 詳解Spring Data JPA動(dòng)態(tài)條件查詢的寫法

    詳解Spring Data JPA動(dòng)態(tài)條件查詢的寫法

    本篇文章主要介紹了Spring Data JPA動(dòng)態(tài)條件查詢的寫法 ,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-06-06
  • java線程的基礎(chǔ)實(shí)例解析

    java線程的基礎(chǔ)實(shí)例解析

    這篇文章主要介紹了java線程的基礎(chǔ)實(shí)例解析,具有一定借鑒價(jià)值,需要的朋友可以參考下
    2017-12-12
  • Java遞歸 遍歷目錄的小例子

    Java遞歸 遍歷目錄的小例子

    Java遞歸 遍歷目錄的小例子,需要的朋友可以參考一下
    2013-03-03
  • 計(jì)算一個(gè)Java對(duì)象占用字節(jié)數(shù)的方法

    計(jì)算一個(gè)Java對(duì)象占用字節(jié)數(shù)的方法

    這篇文章主要介紹了計(jì)算一個(gè)Java對(duì)象占用字節(jié)數(shù)的方法,較為詳細(xì)的分析了Java中各類對(duì)象所占用的字節(jié)數(shù),需要的朋友可以參考下
    2015-01-01
  • Java中兩種拋出異常的方式示例詳解

    Java中兩種拋出異常的方式示例詳解

    在Java中有兩種拋出異常的方式,一種是throw,直接拋出異常,另一種是throws,間接拋出異常,本文給大家詳細(xì)說明java中兩種拋出異常的方式,感興趣的朋友一起看看吧
    2023-08-08
  • Eclipse的Debug調(diào)試技巧大全(總結(jié))

    Eclipse的Debug調(diào)試技巧大全(總結(jié))

    這篇文章主要介紹了Eclipse的Debug調(diào)試技巧大全(總結(jié)),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-12-12
  • Java判斷2個(gè)List集合是否相等(不考慮元素的順序)

    Java判斷2個(gè)List集合是否相等(不考慮元素的順序)

    今天小編就為大家分享一篇關(guān)于Java判斷2個(gè)List集合是否相等(不考慮元素的順序)的文章,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧
    2018-10-10
  • Java Restful API的攔截詳解

    Java Restful API的攔截詳解

    這篇文章主要介紹了Java 調(diào)用Restful API接口的攔截,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2021-09-09

最新評(píng)論

海盐县| 扎兰屯市| 临海市| 孟村| 遵义市| 苗栗市| 绵竹市| 彝良县| 朔州市| 长汀县| 泽州县| 和龙市| 水富县| 友谊县| 洛浦县| 安吉县| 嫩江县| 浏阳市| 马公市| 且末县| 中卫市| 开鲁县| 定兴县| 永春县| 闻喜县| 东至县| 长阳| 西贡区| 宿州市| 胶南市| 班戈县| 桂平市| 洛隆县| 厦门市| 库尔勒市| 塔城市| 峨边| 根河市| 贵港市| 汝南县| 龙口市|