成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Spring的循環(huán)依賴(lài),到底是什么樣的

開(kāi)發(fā) 前端
什么是循環(huán)依賴(lài),說(shuō)到循環(huán)依賴(lài),這個(gè)實(shí)際上是沒(méi)有那么復(fù)雜的,就比如很簡(jiǎn)單的說(shuō),A 引用了 B ,而這個(gè)時(shí)候 B 也引用了 A ,那么這種情況實(shí)際上就是出現(xiàn)了循環(huán)依賴(lài)的問(wèn)題了,實(shí)際上也可以把循環(huán)依賴(lài)稱(chēng)之為循環(huán)引用,兩個(gè)或者兩個(gè)以上的bean互相持有對(duì)方,最終形成閉環(huán)。

前一段時(shí)間,阿粉的讀者給阿粉留言,說(shuō)在面試的時(shí)候,有個(gè)面試官就問(wèn)她,Spring 的各種知識(shí),Spring 的生命周期, Spring 的循環(huán)依賴(lài)是如何解決的。

就這么幾個(gè)問(wèn)題,雖然回答的不是很好,但是也是很幸運(yùn)的接到了 offer ,畢竟面試一般很少會(huì)因?yàn)橐粌蓚€(gè)面試題回答的不好,就直接 pass 的,還是看綜合表現(xiàn)的,既然問(wèn)到阿粉這個(gè) Spring 是如何處理循環(huán)依賴(lài)的了,那么阿粉就得來(lái)解釋一下,Spring 是如何處理循環(huán)依賴(lài)的。

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

什么是循環(huán)依賴(lài),說(shuō)到循環(huán)依賴(lài),這個(gè)實(shí)際上是沒(méi)有那么復(fù)雜的,就比如很簡(jiǎn)單的說(shuō),A 引用了 B ,而這個(gè)時(shí)候 B 也引用了 A ,那么這種情況實(shí)際上就是出現(xiàn)了循環(huán)依賴(lài)的問(wèn)題了,實(shí)際上也可以把循環(huán)依賴(lài)稱(chēng)之為循環(huán)引用,兩個(gè)或者兩個(gè)以上的bean互相持有對(duì)方,最終形成閉環(huán)。

這就是循環(huán)依賴(lài),也就是循環(huán)引用,

圖片

注意,這里不是函數(shù)的循環(huán)調(diào)用,是對(duì)象的相互依賴(lài)關(guān)系。循環(huán)調(diào)用其實(shí)就是一個(gè)死循環(huán),除非有終結(jié)條件。否則的話,他就是一個(gè)死循環(huán).

Spring 中的循環(huán)依賴(lài)

那么 Spring 的循環(huán)依賴(lài)都有什么呢?

  • 構(gòu)造器的循環(huán)依賴(lài)
  • field屬性的循環(huán)依賴(lài)

那么針對(duì)這兩種循環(huán)依賴(lài),Spring 它是如何解決的呢?這就很特殊了,構(gòu)造器的循環(huán)依賴(lài)問(wèn)題實(shí)際上算是個(gè)無(wú)解的操作,只能拋出 BeanCurrentlyInCreationException 異常,也就是說(shuō),這個(gè)構(gòu)造器導(dǎo)致的循環(huán)依賴(lài),Spring 是沒(méi)有辦法來(lái)處理的,也只是給拋出了異常,但是對(duì)于 字段屬性 的循環(huán)依賴(lài),還是有解決辦法的。

Spring怎么解決循環(huán)依賴(lài)

這個(gè)時(shí)候,我們就得看看 Spring 的對(duì)象初始化的過(guò)程了,

Spring的單例對(duì)象的初始化主要分為三步:

  • createBeanInstance 實(shí)例化
  • populateBean 填充屬性
  • initializeBean 初始化

createBeanInstance 實(shí)例化實(shí)際上就是調(diào)用對(duì)象的構(gòu)造方法實(shí)例化對(duì)象,populateBean 實(shí)際上就是對(duì) bean 的依賴(lài)屬性進(jìn)行一個(gè)賦值填充,而 initializeBean 則是調(diào)用 Spring xml 中的 init 方法。

這個(gè)時(shí)候,我們看到這個(gè)初始化的過(guò)程,一般就應(yīng)該能猜到會(huì)發(fā)生 循環(huán)依賴(lài)? 的位置是哪一步了,而單從 bean 的初始化來(lái)看,循環(huán)依賴(lài)發(fā)生的位置就是在 createBeanInstance 實(shí)例化? 以及 populateBean 填充屬性 當(dāng)中,

發(fā)生的循環(huán)依賴(lài)也是

  • 構(gòu)造器的循環(huán)依賴(lài)
  • field屬性的循環(huán)依賴(lài)

那么 Spring 又是怎么解決這種單例的循環(huán)依賴(lài)的問(wèn)題的呢?

?三級(jí)緩存

那么這三級(jí)緩存分別是哪三級(jí)的緩存呢?又分別代表了什么含義?

  • singletonFactories :?jiǎn)卫龑?duì)象工廠的cache,用于存放完全初始化好的 bean,從該緩存中取出的 bean 可以直接使用
  • earlySingletonObjects :提前暴光的單例對(duì)象的Cache,存放原始的 bean 對(duì)象(尚未填充屬性),用于解決循環(huán)依賴(lài)
  • singletonObjects:?jiǎn)卫龑?duì)象的cache,存放 bean 工廠對(duì)象,用于解決循環(huán)依賴(lài)
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); //一級(jí)緩存
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16); // 二級(jí)緩存
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); // 三級(jí)緩存

如果要分析這個(gè) 三級(jí)緩存 如何解決循環(huán)依賴(lài),那么勢(shì)必需要知道 Spring 中對(duì)象的創(chuàng)建的過(guò)程。

對(duì)象創(chuàng)建過(guò)程,可以大致分為五個(gè)步驟,

1.protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly)

AbstractBeanFactory? 中的 doGetBean()方法

2.protected Object getSingleton(String beanName, boolean allowEarlyReference)

DefaultSingletonBeanRegistry? 中的 getSingleton()方法

  • 在這個(gè)方法中,先從一級(jí)緩存 singletonObjects 中去獲取。(如果獲取到就直接return)
  • 如果獲取不到,并且對(duì)象正在創(chuàng)建中,就再?gòu)亩?jí)緩存 earlySingletonObjects 中獲取。
  • 如果還是獲取不到且允許 singletonFactories? 通過(guò) getObject()? 獲取,就從三級(jí)緩存singletonFactory.getObject()(三級(jí)緩存)獲取
  • 如果獲取到了則:從 singletonFactories? 中移除,并放入 earlySingletonObjects 中
  • 這就相當(dāng)于 ctrl+x ,把三級(jí)緩存中的數(shù)據(jù)剪切到了二級(jí)緩存。

源碼如下:

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}

3.protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)

AbstractAutowireCapableBeanFactory? 中的 doCreateBean() 方法

//添加到三級(jí)緩存
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}

4.protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw)

AbstractAutowireCapableBeanFactory? 中的 populateBean() 方法進(jìn)行屬性賦值

5.protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd)

AbstractAutowireCapableBeanFactory? 中的 initializeBean() 初始化對(duì)象

源碼部分阿粉就不再往上貼那么多了,大家找源碼肯定很簡(jiǎn)單,內(nèi)部也有具體方法的注釋?zhuān)?/p>

Spring 解決循環(huán)依賴(lài)的訣竅就在于 singletonFactories 這個(gè)三級(jí)cache。

這個(gè) cache 的類(lèi)型是 ObjectFactory?。這里就是解決循環(huán)依賴(lài)的關(guān)鍵,發(fā)生在createBeanInstance之后,也就是說(shuō)單例對(duì)象此時(shí)已經(jīng)被創(chuàng)建出來(lái)(調(diào)用了構(gòu)造器)。

這個(gè)對(duì)象已經(jīng)被生產(chǎn)出來(lái)了,雖然還不完美(還沒(méi)有進(jìn)行初始化的第二步和第三步),但是已經(jīng)能被人認(rèn)出來(lái)了(根據(jù)對(duì)象引用能定位到堆中的對(duì)象),所以Spring此時(shí)將這個(gè)對(duì)象提前曝光出來(lái)讓大家認(rèn)識(shí),讓大家使用。

如果你能在面試的時(shí)候,回答成這個(gè)樣子,那么這個(gè)問(wèn)題,你至少已經(jīng)算是回答的比較好了。

但是如果問(wèn)到這里,面試官有意想要繼續(xù)深挖一下,你既然知道使用三級(jí)緩存解決了這個(gè)循環(huán)依賴(lài)的問(wèn)題了,那么是不是必須三級(jí)緩存才能解決,二級(jí)緩存不能解決嗎?

這就另外又給你引申出一個(gè)問(wèn)題來(lái)了,二級(jí)緩存到底能不能解決呢?

其實(shí),二級(jí)緩存也是能夠?qū)崿F(xiàn)的,如果自己想要實(shí)現(xiàn),那么就得去改寫(xiě) AbstractAutowireCapableBeanFactory? 的 doCreateBean 的方法了,

//添加到三級(jí)緩存
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
//從三級(jí)緩存中取出立刻放入二級(jí)緩存
getSingleton(beanName, true);
}

如果要使用二級(jí)緩存解決循環(huán)依賴(lài),意味著B(niǎo)ean在構(gòu)造完后就創(chuàng)建代理對(duì)象,這樣違背了Spring設(shè)計(jì)原則。

Spring結(jié)合AOP跟Bean的生命周期,是在Bean創(chuàng)建完全之后通過(guò)AnnotationAwareAspectJAutoProxyCreator這個(gè)后置處理器來(lái)完成的,在這個(gè)后置處理的postProcessAfterInitialization方法中對(duì)初始化后的Bean完成AOP代理。

如果出現(xiàn)了循環(huán)依賴(lài),那沒(méi)有辦法,只有給Bean先創(chuàng)建代理,但是沒(méi)有出現(xiàn)循環(huán)依賴(lài)的情況下,設(shè)計(jì)之初就是讓Bean在生命周期的最后一步完成代理而不是在實(shí)例化后就立馬完成代理。

所以,你知道為什么不使用二級(jí)緩存直接來(lái)處理了,而是增加了三級(jí)緩存來(lái)處理這個(gè)循環(huán)依賴(lài)了吧!


責(zé)任編輯:武曉燕 來(lái)源: Java極客技術(shù)
相關(guān)推薦

2009-03-25 09:45:15

美國(guó)軟件公司工作環(huán)境

2013-05-16 10:00:11

2010-06-07 13:47:39

培訓(xùn)

2023-11-28 08:00:00

SpringJava

2020-11-06 17:49:38

程序員技術(shù)開(kāi)發(fā)

2022-02-28 10:16:12

算力網(wǎng)絡(luò)新基建東數(shù)西算

2022-10-08 00:00:00

Spring數(shù)據(jù)庫(kù)項(xiàng)目

2020-03-05 10:28:19

MySQLMRR磁盤(pán)讀

2009-10-26 13:36:10

BSM

2009-06-09 22:11:44

JavaScriptObject

2023-10-11 08:29:54

volatileJava原子性

2023-01-06 21:03:59

2013-08-29 11:38:53

企業(yè)App

2011-04-27 09:30:48

企業(yè)架構(gòu)

2020-09-27 06:53:57

MavenCDNwrapper

2020-09-22 08:22:28

快充

2020-10-14 06:22:14

UWB技術(shù)感知

2010-11-01 01:25:36

Windows NT

2022-10-30 15:03:25

人工智能倉(cāng)庫(kù)管理機(jī)器人

2017-12-17 16:53:27

云計(jì)算亞馬遜云端
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 欧美成人一区二区三区片免费 | 中文字幕在线精品 | 亚洲欧美视频 | 欧美成人一级 | 欧美成人猛片aaaaaaa | 超碰97人人人人人蜜桃 | 欧美性受xxxx | 亚洲精品中文字幕在线观看 | 日韩a视频 | 亚欧洲精品在线视频免费观看 | 免费在线观看一区二区三区 | 日韩一区精品 | 国产ts一区 | 色综合中文| 日韩欧美三区 | 天天干天天谢 | 国产丝袜av | 国产综合在线视频 | 久久精品a级毛片 | 欧美黄在线观看 | ww亚洲ww亚在线观看 | 日韩午夜 | 国产精品久久久久免费 | 欧洲性生活视频 | 久久久久国产 | 国产成人免费视频网站高清观看视频 | 欧美日韩国产一区二区三区 | 久久蜜桃资源一区二区老牛 | 成人av播放| 欧美激情亚洲激情 | 久久久久久久久久久久久9999 | 91黄在线观看 | 成人精品国产一区二区4080 | 国产中文| 国产一区久久 | 99视频免费在线观看 | 一区二区三区国产精品 | 日韩精品免费在线观看 | 在线一区 | 在线免费国产 | 亚洲精品久久久久久久久久久 |