逐行閱讀Spring5.X源碼(十)spring如何解決循環(huán)引用培廓,bean實例化過程源碼詳解

前面講了Spring的核心基礎BeanDefinition及bean工廠后置處理器惹悄,這兩塊內(nèi)容主要作用概括來講就是定義、掃描肩钠、注冊泣港、動態(tài)代理等,這些基礎工作做完之后价匠,spring就要把我們的業(yè)務類(或代理業(yè)務類)進行實例化了当纱,即bean生命周期管理,這又是spring中又一大核心和難點踩窖,今天我們先了解一下bean的實例化坡氯!

首先祭出spring啟動測試類

public class SpringTest {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(Config.class);
        context.refresh();
    }
}

bean的實例化是在refresh()——>finishBeanFactoryInitialization(beanFactory);方法里完成的。當然毙石,只能實例化單例的類廉沮。

什么是循環(huán)引用

下面的代碼中,兩個類相互引用徐矩,就是循環(huán)依賴。


@Component
public class OrderService {

    @Autowired
    UserService userService;
    
    public OrderService(){
        System.out.println("OrderService——>"+userService);
    }
}



@Component
public class UserService {

    @Autowired
    OrderService orderService;

    public UserService(){
        System.out.println("UserService->" + orderService);
    }
}

這兩個類非常簡單叁幢,就是相互引用了對方滤灯,也就是我們常常的說的循環(huán)依賴,spring是允許這樣的循環(huán)依賴(前提是單例的情況下的,非構(gòu)造方法注入的情況下)。

spring默認是支持循序依賴的鳞骤,但是僅僅是單例的類才可以窒百。上面代碼從容器中能正常獲取到bean,說明循環(huán)依賴成功豫尽。但是spring的循環(huán)依賴其實是可以關閉的篙梢,spring提供了api來關閉循環(huán)依賴的功能。spring也可以關閉循環(huán)依賴:

public class SpringTest {

    public static void main(String[] args) {

        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        //獲取bean工廠
        DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) context.getBeanFactory();
        //關閉循環(huán)依賴
        beanFactory.setAllowCircularReferences(false);
        context.register(Config.class);
        context.refresh();
        System.out.println(context.getBean("orderService"));
    }
}

運行上面的代碼美旧,就會報錯


那么為什么setAllowCircularReferences(false);會關閉循環(huán)依賴呢渤滞?首要明白spring的循環(huán)依賴是怎么做到的呢?spring源碼當中是如何處理循環(huán)依賴的榴嗅? 分析一下所謂的循環(huán)依賴其實無非就是屬性注入妄呕,或者就是大家常常說的自動注入, 故而搞明白循環(huán)依賴就需要去研究spring自動注入的源碼嗽测;spring的屬性注入屬于spring bean的生命周期一部分绪励;怎么理解spring bean的生命周期呢?注意筆者這里并不打算對bean的生命周期大書特書唠粥,只是需要讀者理解生命周期的概念疏魏,細節(jié)以后在計較;

要理解bean的生命周期首先記住兩個概念
請讀者一定記住兩個概念——spring bean(一下簡稱bean)和對象晤愧;
1大莫、spring bean——受spring容器管理的對象,可能經(jīng)過了完整的spring bean生命周期(為什么是可能养涮?難道還有bean是沒有經(jīng)過bean生命周期的葵硕?答案是有的,具體我們后面文章分析)贯吓,最終存在spring容器當中懈凹;一個bean一定是個對象
2、對象——任何符合java語法規(guī)則實例化出來的對象悄谐,但是一個對象并不一定是spring bean介评;

所謂的bean的生命周期就是磁盤上的類通過spring掃描,然后實例化爬舰,跟著初始化们陆,繼而放到容器當中的過程;
我畫了一張簡單圖來闡述一下spring bean的生命周期大概有哪些步驟


上圖就是spring容器初始化bean的大概過程(至于詳細的過程情屹,后面文章再來介紹)坪仇;
文字總結(jié)一下:
1:實例化一個ApplicationContext的對象;
2:調(diào)用bean工廠后置處理器完成掃描垃你;
3:循環(huán)解析掃描出來的類信息椅文;
4:實例化一個BeanDefinition對象來存儲解析出來的信息喂很;
5:把實例化好的beanDefinition對象put到beanDefinitionMap當中緩存起來,以便后面實例化bean皆刺;
6:再次調(diào)用bean工廠后置處理器少辣;
7:當然spring還會干很多事情,比如國際化羡蛾,比如注冊BeanPostProcessor等等漓帅,如果我們只關心如何實例化一個bean的話那么這一步就是spring調(diào)用finishBeanFactoryInitialization方法來實例化單例的bean,實例化之前spring要做驗證痴怨,需要遍歷所有掃描出來的類忙干,依次判斷這個bean是否Lazy,是否prototype腿箩,是否abstract等等豪直;
8:如果驗證完成spring在實例化一個bean之前需要推斷構(gòu)造方法,因為spring實例化對象是通過構(gòu)造方法反射珠移,故而需要知道用哪個構(gòu)造方法弓乙;
9:推斷完構(gòu)造方法之后spring調(diào)用構(gòu)造方法反射實例化一個對象;注意我這里說的是對象钧惧、對象暇韧、對象;這個時候?qū)ο笠呀?jīng)實例化出來了浓瞪,但是并不是一個完整的bean懈玻,最簡單的體現(xiàn)是這個時候?qū)嵗鰜淼膶ο髮傩允菦]有注入,所以不是一個完整的bean乾颁;
10:spring處理合并后的beanDefinition(合并涂乌?是spring當中非常重要的一塊內(nèi)容,后面的文章我會分析)英岭;
11:判斷是否支持循環(huán)依賴湾盒,如果支持則提前把一個工廠存入singletonFactories——map;
12:判斷是否需要完成屬性注入
13:如果需要完成屬性注入诅妹,則開始注入屬性
14:判斷bean的類型回調(diào)Aware接口
15:調(diào)用生命周期回調(diào)方法
16:如果需要代理則完成代理
17:put到單例池——bean完成——存在spring容器當中

用一個例子來證明上面的步驟罚勾,

@Component
public class X {
    @Autowired
    Y y;
    public X(){
        System.out.println("X create");
    }
}

@Component
public class Y {
    @Autowired
    X x;
    public Y(){
        System.out.println("Y create");
    }
}


@Component
public class Z implements ApplicationContextAware {
    @Autowired
    X x;//注入X
    //構(gòu)造方法
    public Z(){
        System.out.println("Z create");
    }
    //生命周期初始化回調(diào)方法
    @PostConstruct
    public void zinit(){
        System.out.println("call z lifecycle init callback");
    }
    //ApplicationContextAware 回調(diào)方法
    @Override
    public void setApplicationContext(ApplicationContext ac) {
        System.out.println("call aware callback");
    }
}

來看看Z的生命周期,注意下圖當中的字幕吭狡,會和上面的17個步驟一一對應

下圖是第一步到第六步尖殃,請自行對應

接下來我們通過各種圖片分析一下springbean的生命周期,讀者只需要看圖搞明白流程划煮,至于圖中涉及的源碼送丰,分析完流程之后再來解釋;


步驟1~6.gif

在研究其他步驟之前弛秋,首先了解spring大概在什么時候?qū)嵗痓ean的


finishBeanFactoryInitialization方法開始實例化bean.gif

上圖可以知道spring在AbstractApplicationContext#finishBeanFactoryInitialization方法中完成了bean的實例化蚪战。這點需要記住
然后通過圖片來說明一下第7步
20191119171834279.gif

接下來spring需要推斷構(gòu)造方法牵现,然后通過推斷出來的構(gòu)造方法反射實例化對象铐懊,也就是上面說的第8步和第9步


20191119205040133.gif

上圖說明spring是通過createBeanInstance(beanName, mbd, args);完成了推斷構(gòu)造方法和實例化的事情那么接下來便要執(zhí)行第10步處理合并后的beanDefinition對象邀桑,這一塊內(nèi)容特別多,讀者可以先不必要理解科乎,后面文章會解釋壁畸;



仔細看上圖,其實這個時候雖然Z被實例化出來了茅茂,但是并沒有完成屬性的注入捏萍;其中的X屬性為null,而且里面的Aware接口的方法也沒有調(diào)用空闲,再就是@PostConstruct方法也沒有調(diào)用令杈,再一次說明他不是一個完整的bean,這里我們只能說z是個對象碴倾;
繼而applyMergedBeanDefinitionPostProcessors方法就是用來處理合并后的beanDefinition對象逗噩;

跟著第11步,判斷是否支持循環(huán)依賴跌榔,如果支持則提前暴露一個工廠對象异雁,注意是工廠對象


第12步,spring會判斷是否需要完成屬性注入(spring默認是需要的僧须,但是程序員可以擴展spring纲刀,根據(jù)情況是否需要完成屬性注入);如果需要則spring完成13步——屬性注入担平,也就是所謂的自動注入示绊;


第14、15暂论、16步


默認情況 至此一個bean完成初始化面褐,被put到單例池抡笼,也是對上文說的17個步驟的一個證明夹姥;這說明一個bean在spring容器當中被創(chuàng)建出來是有一個過程的苏章,這個過程就是所謂的bean的生命周期颖杏,我們的循環(huán)依賴也是在這個生命周內(nèi)完成的映挂。

下面我們具體來分析這些步驟

由于bean的生命周期特別復雜本文只對涉及到循環(huán)依賴的步驟做分析,其他生命周期的步驟我會在后續(xù)博客中分析锐极,可以繼續(xù)關注本專題

回顧上面的圖② 和 圖③ 我們知道spring的bean是在AbstractApplicationContext#finishBeanFactoryInitialization()方法完成的初始化,即循環(huán)依賴也在這個方法里面完成的全陨。該方法里面調(diào)用了一個非常重要的方法 doGetBean的方法

照例用圖片來說明一下吧


doGetBean方法內(nèi)容有點多,這個方法非常重要颅拦,不僅僅針對循環(huán)依賴哮肚,甚至整個spring bean生命周期中這個方法也有著舉足輕重的地位,讀者可以認真看看筆者的分析。需要說明的是我為了更好的說清楚這個方法,我把代碼放到文章里面進行分析删顶;但是刪除了一些無用的代碼竖螃;比如日志的記錄這些無關緊要的代碼。下面重點說這個doGetBean方法
首先筆者把精簡后的代碼貼出來方便大家閱讀

protected <T> T doGetBean(final String name, 
                    @Nullable final Class<T> requiredType,
                    @Nullable final Object[] args, 
                    boolean typeCheckOnly)
                    throws BeansException {
    //讀者可以簡單的認為就是對beanName做一個校驗特殊字符串的功能
    //我會在下次更新博客的時候重點討論這個方法
    //transformedBeanName(name)這里的name就是bean的名字
   final String beanName = transformedBeanName(name);
   
   //定義了一個對象翼闹,用來存將來返回出來的bean
   Object bean;

    //deGetBean-1
   Object sharedInstance = getSingleton(beanName);
   
    //deGetBean-2
    if (sharedInstance != null && args == null) {
      bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
   }else{
        ////deGetBean-3
        if (isPrototypeCurrentlyInCreation(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
      }else{
        //doGetBean-4
        if (mbd.isSingleton()) {
            sharedInstance = getSingleton(beanName, () -> {
               try {
                  return createBean(beanName, mbd, args);
               }
               catch (BeansException ex) {
                  destroySingleton(beanName);
                  throw ex;
               }
            });
      }
   }
 }

注意:上面的代碼是我對doGetBean方法進行了刪減的代碼斑鼻,只保留了和本文討論的循環(huán)依賴有關的代碼,完整版可以參考spring的源碼org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean

接著筆者對上述代碼逐行來解釋

1猎荠、deGetBean-1

Object sharedInstance = getSingleton(beanName);


首先這行代碼上有一句spring作者寫的注釋
Eagerly check singleton cache for manually registered singletons.
大概的意思就是檢查一下單例池當中有沒有手動注冊的單例對象坚弱,說白了spring在創(chuàng)建一個bean之前先檢查一下beanName是否被手動注冊過到單例池當中蜀备;別小看這句spring作者寫的javadoc背后的意義,其實這里有兩重意思荒叶;要搞清楚這兩重意思首先知道當代碼執(zhí)行到這里的時候其實是spring在初始化的時候執(zhí)行過來的碾阁;既然spring在初始化的時候他肯定知道這個類X.java肯定沒有在容器當中,為什么還需要去驗證一下呢些楣?好比說你第一次去天上人間脂凶,你幾乎都能確定這是你一次去你不可能跑到那里問一下前臺你有沒有辦會員吧?但是spring確這樣做了愁茁,他問了蚕钦,他問問自己有沒有辦會員;為什么呢鹅很?回到你自己嘶居,如果你去問自己有沒有辦會員無非就是怕別人拿著你的身份證去辦了一個會員,或者各種原因陰差陽錯別人吧身份證名字寫錯了促煮,導致你成了天上人間的會員邮屁;其實spring也是這個意思,因為一個bean被put到單例池的渠道有很多菠齿;除了spring容器初始化—掃描類----實例化-----put到容器這條線之外還有很多方法可以把一個對象put到單例池佑吝;我這里只列舉一種,其他的有機會再討論绳匀,看下圖 注意注釋芋忿;

這就相當于在你第一次抱著緊張心態(tài)去天上人間的時候,發(fā)現(xiàn)你朋友以前拿著你的身份證去那里辦了一個會員卡一樣襟士;
所以上面提到的這句注釋的兩重意思①第一盗飒,判斷spring當前正準備初始化的bean有沒有提前被put到容器;
那么第二重意思是什么呢陋桂?既然這里用來做spring初始化的工作逆趣,為什么這個方法名叫做doGetBean呢?講道理應該叫做createBean啊才合理笆壤宣渗;有讀者可能會說這個方法命名可能作者亂寫的,請注意spring之所以經(jīng)久不衰命名規(guī)范絕對是一個重要原因梨州,作者是不會這么亂給方法命名的痕囱。誠然有的讀者會說討論這個的意義不大,其實博主覺得討論這個非常重要暴匠;之所這里叫做doGetBean的原因就是因為這個方法就是用來獲取bean的鞍恢,他主要的工作不僅僅服務于spring bean的初始化;這個方法的作用不僅僅是為了spring 在初始化bean的過程中去判斷一下這個bean是否被注冊了這么簡單;筆者認為這個方法最主要的作用是為了從容器中得到一個bean帮掉,也就是說當我們在spring代碼中調(diào)用getBean(“a”)其背后的意義就是調(diào)用這個doGetBean弦悉,同樣用一段代碼來證明



可以看到當我調(diào)用ac.getBean(“x”)的時候,底層其實就調(diào)用doGetBean獲取這X對象的蟆炊;spring之所以這么設計就是因為判斷bean是否初始化好和get一個bean都需要從單例池當中獲取稽莉,所以創(chuàng)建bean和getBean都需要調(diào)用這個doGetBean方法;也就是第②重意思涩搓,這個方法其實就是程序員getBean的底層實現(xiàn)污秆;

換成天上人間,你第一次跑去前臺昧甘,人家前臺直接說:先生請出示會員卡良拼;你可能會奇怪——我是來全套的,你應該問我要什么服務疾层,不是問會員卡将饺;但是人家前臺的職責有兩,辦會員和問你要什么服務痛黎;所以才會說出這句話;doGetBean也是這個意思刮吧,于是解釋了這個方法名的意義了湖饱;

總結(jié)一下 Object sharedInstance = getSingleton(beanName);目前看來主要是用于在spring初始化bean的時候判斷bean是否在容器當中;以及供程序員直接get某個bean杀捻。

注意筆者這里用了 目前這個詞井厌;因為getSingleton(beanName);這個方法代碼比較多;他里面的邏輯是實現(xiàn)循環(huán)依賴最主要的代碼致讥,文章下面我會回過頭再來講這個方法的全部意義仅仆;

請注意我們當前代碼的場景,當前代碼是spring容器在初始化的時候垢袱,初始化X這個bean的場景墓拜;運行到了Object sharedInstance = getSingleton(beanName);
根據(jù)上面的分析,這個時候我的X Bean肯定沒有被創(chuàng)建请契,所以這里返回sharedInstance = =null咳榜;

跟著解析 //deGetBean-2

//deGetBean-2
if (sharedInstance != null && args == null) {
  bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);

由于 sharedInstance = =null 故而不會進入這個if分支,那么什么時候不等于null呢爽锥?兩種情況1涌韩、在spring初始化完成后程序員調(diào)用getBean(“x”)的時候得到的sharedInstance 就不等于null;2氯夷、循環(huán)依賴的時候第二次獲取對象的時候這里也不等于空臣樱;比如X 依賴 Y;Y依賴X;spring做初始化第一次執(zhí)行到這里的時候X 肯定等于null雇毫,然后接著往下執(zhí)行玄捕,當執(zhí)行到屬性注入Y的時候,Y也會執(zhí)行到這里嘴拢,那么Y也是null桩盲,因為Y也沒初始化,Y也會接著往下執(zhí)行席吴,當Y執(zhí)行到屬性注入的時候獲取容器中獲取X赌结,也就是第二次執(zhí)行獲取X;這個時候X則不為空孝冒;至于具體原因柬姚,讀者接著往下看;
至于這個if分支里面的代碼干了什么事情庄涡,本文不討論量承,放到后面寫factoryBean的時候討論,現(xiàn)在你可以理解if分支里面就把sharedInstance 原原本本的返回出來就行穴店;即這個if分支沒有意義撕捍;

上文說了本次不進入if分支,所以這行代碼解析完畢泣洞;

接下解析 doGetBean -3

else{
        ////deGetBean-3
        if (isPrototypeCurrentlyInCreation(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
      }
如果把throw刪了可能更加清晰吧忧风,下面是刪除后的代碼
if (isPrototypeCurrentlyInCreation(beanName)) {}

不進if分支,則進入這個else分支球凰,把throw刪了 就一句代碼狮腿;判斷當前初始化的bean----X 是不是正在創(chuàng)建原型bean集合當中當中?
spring源碼當中關于這行代碼有兩行javadoc

// Fail if we're already creating this bean instance:
// We're assumably within a circular reference.

比較簡單我就不翻譯了呕诉,一般情況下這里返回false缘厢,也就是不會進入if分支拋異常;為什么呢說一般情況下呢甩挫?首先這里是判斷當前的類是不是正在創(chuàng)建的原型集合當中贴硫,即里面只會存原型;一般情況下我們的類不是原型捶闸,而是單例的夜畴,大家都知道spring默認是單例;所以返回false删壮,再就是即使這個bean是原型也很少會在這里就存在正在創(chuàng)建的原型集合當中贪绘。因為不管單例還是原型,bean在創(chuàng)建的過程中會add到這個集合當中央碟,但是創(chuàng)建完成之后就會從這個集合remove掉(關于這個文章后面有證明)税灌,原型情況第一次創(chuàng)建的時候會add到這個集合均函,但是不是在這里,而是在后面的創(chuàng)建過程中add菱涤,所以這里肯定不會存在苞也,即使后面過程中add到這個集合了,但是創(chuàng)建完成之后也會remove掉粘秆,故而下一次實例化同一個原型bean(原型可以實例化無數(shù)次)的時候當代碼執(zhí)行到這里也不可能存在集合當中了如迟;除非循環(huán)依賴會在bean還沒有在這個集合remove之前再次判斷一次,才有可能會存在攻走,故而我前面說了一般情況下這里都返回false殷勘;那么單例情況我們已經(jīng)說了一定返回false,原型情況只有循環(huán)依賴才會成立昔搂,但是只要是正常人就不會對原型對象做循環(huán)依賴的玲销;即使你用原型做了循環(huán)依賴這里也出拋異常(因為if成立,進入分支 throw exception)摘符。再一次說明原型不支持循環(huán)依賴(當然你非得用原型做循環(huán)依賴贤斜,其實有辦法,以后文章說明逛裤,本文忽略)瘩绒;畫了一幅圖說明上面的文字,因為這個集合非常重要带族,但是讀者如果這里不理解也沒關系草讶,文章下面我還會結(jié)合代碼分析一次;

重點來了:說明叫做正在創(chuàng)建的原型集合呢炉菲? 還有一個與之對應的叫做正在創(chuàng)建的單例集合
唯一的區(qū)別就是集合里面存的是單例和原型
故而我們統(tǒng)稱正在創(chuàng)建的集合,關于正在創(chuàng)建的集合是什么我下面會解釋
但是需要記住的坤溃,這個集合是我的一家之言拍霜,說白了這是筆者自己翻譯的,叫做正在創(chuàng)建的集合薪介,沒有官方支持祠饺,至少我也沒在書上看到過這個名詞

下面解析doGetBean-4

else{
        //doGetBean-4
        if (mbd.isSingleton()) {
            sharedInstance = getSingleton(beanName, () -> {
               try {
                  return createBean(beanName, mbd, args);
               }
               catch (BeansException ex) {
                  destroySingleton(beanName);
                  throw ex;
               }
            });
   同樣把拋異常的代碼刪了,如下
    //doGetBean-4
        if (mbd.isSingleton()) {
            sharedInstance = getSingleton(beanName, () -> {
                  return createBean(beanName, mbd, args);
            });

代碼有點多汁政;if (mbd.isSingleton()) 比較簡單道偷,判斷當前bean是否單例;本文環(huán)境下是成立的记劈;繼而

sharedInstance = getSingleton(beanName, () -> {
                  return createBean(beanName, mbd, args);
            });

這里又調(diào)用了一次getSingleton勺鸦,如果有印象上面也調(diào)用了一次getSingleton,這是方法重載目木,兩個getSingleton方法并不是同一個方法换途,讀者自己看參數(shù)就行,為了區(qū)別我這這里叫做第二次調(diào)用getSingleton;上文的叫做第一次調(diào)用getSingleton军拟;由于這里使用lamda表達式剃执,有些讀者看起來不是很理解;筆者改一下吧

ObjectFactory<?>  singletonFactory = new ObjectFactory(){
    public Object getObject(){
        //其實這是個抽象類懈息,不能實例化
        //createBean是子類實現(xiàn)的肾档,這里就不關心了
        //你就理解這不是一個抽象類吧
        AbstractBeanFactory abf = new AbstractBeanFactory();
        Object bean = abf.createBean(beanName, mbd, args);
        return bean;
    };
};
//傳入 beanName 和singletonFactory 對象
sharedInstance = getSingleton(beanName,singletonFactory);

這樣看是不是明白多了呢?

當然第二次getSingleton就會把我們bean創(chuàng)建出來辫继,換言之整個bean如何被初始化的都是在這個方法里面怒见;至此本文當中筆者例舉出來的doGetBean方法的核心代碼看起來解析完成了;

注意我說的是本文當中例舉的doGetBean代碼骇两,前面我已經(jīng)說了我刪了很多和循環(huán)依賴無關的代碼速种,實際spring源碼當中這個方法的代碼很多,以后文章介紹吧低千;

接下來就要研究第二次getSingleton方法的內(nèi)容了配阵,因為我說了整個bean初始化過程都在里面體現(xiàn)了;

我先把spring源碼貼出來示血,讀者可以忽略這里棋傍,因為下面會精簡代碼;之所以貼出源碼就是想告訴讀者难审,為了研究循環(huán)依賴瘫拣,本文中的很代碼我是做了刪減的;

spring源碼:-----讀者可以忽略
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
        Assert.notNull(beanName, "Bean name must not be null");
        synchronized (this.singletonObjects) {
            Object singletonObject = this.singletonObjects.get(beanName);
            if (singletonObject == null) {
                if (this.singletonsCurrentlyInDestruction) {
                    throw new BeanCreationNotAllowedException(beanName,
                            "Singleton bean creation not allowed while singletons of this factory are in destruction " +
                            "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
                }
                if (logger.isDebugEnabled()) {
                    logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
                }
                beforeSingletonCreation(beanName);
                boolean newSingleton = false;
                boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
                if (recordSuppressedExceptions) {
                    this.suppressedExceptions = new LinkedHashSet<>();
                }
                try {
                    singletonObject = singletonFactory.getObject();
                    newSingleton = true;
                }
                catch (IllegalStateException ex) {
                    // Has the singleton object implicitly appeared in the meantime ->
                    // if yes, proceed with it since the exception indicates that state.
                    singletonObject = this.singletonObjects.get(beanName);
                    if (singletonObject == null) {
                        throw ex;
                    }
                }
                catch (BeanCreationException ex) {
                    if (recordSuppressedExceptions) {
                        for (Exception suppressedException : this.suppressedExceptions) {
                            ex.addRelatedCause(suppressedException);
                        }
                    }
                    throw ex;
                }
                finally {
                    if (recordSuppressedExceptions) {
                        this.suppressedExceptions = null;
                    }
                    afterSingletonCreation(beanName);
                }
                if (newSingleton) {
                    addSingleton(beanName, singletonObject);
                }
            }
            return singletonObject;
        }
    }

下面是我刪減后只和循環(huán)依賴有關的代碼

public Object getSingleton(String beanName, ObjectFactory<?> 
singletonFactory) {
    //getSingleton2 -1
    Object singletonObject = this.singletonObjects.get(beanName);
            //getSingleton2 -2
            if (singletonObject == null) {
                //getSingleton2 -3
                if (this.singletonsCurrentlyInDestruction) {
                    throw new Exception(beanName,
                            "excepition");
                }
                //getSingleton2 -4
                beforeSingletonCreation(beanName);
                //getSingleton2 -5
                singletonObject = singletonFactory.getObject(); 
            }
            return singletonObject;
        }

//getSingleton2 -1 開始解析

Object singletonObject = this.singletonObjects.get(beanName);

第二次getSingleton上來便調(diào)用了this.singletonObjects.get(beanName)告喊,直接從單例池當中獲取這個對象麸拄,由于這里是創(chuàng)建故而一定返回null;singletonObjects是一個map集合黔姜,即所謂的單例池拢切;用大白話說spring所有的單例bean實例化好都存放在這個map當中,這也是很多讀者以前認為的spring容器秆吵,但是筆者想說這種理解是錯誤的淮椰,因為spring容器的概念比較抽象,而單例池只是spring容器的一個組件而已纳寂;但是你如果一定要找一個平衡的說法主穗,只能說這個map——singletonObjects僅僅是狹義上的容器;比如你的原型bean便不在這個map當中毙芜,所以是狹義的spring容器忽媒;下圖為這個map在spring源碼當中的定義

//getSingleton2 -2 開始解析

if (singletonObject == null) {
上面解釋了,在spring 初始化bean的時候這里肯定為空爷肝,故而成立

//getSingleton2 -3 開始解析

if (this.singletonsCurrentlyInDestruction) {
            throw new Exception(beanName,
                    "excepition");
        }

這行代碼其實比較簡單猾浦,判斷當前實例化的bean是否正在銷毀的集合里面陆错;spring不管銷毀還是創(chuàng)建一個bean的過程都比較繁瑣,都會先把他們放到一個集合當中標識正在創(chuàng)建或者銷毀金赦;所以如果你理解了前面那個正在創(chuàng)建集合那么這個正在銷毀集合也就理解了音瓷;但是不理解也沒關系,下面會分析這些集合夹抗;

如果一個bean正在創(chuàng)建绳慎,但是又正在銷毀那么則會出異常;為什么會有這種情況漠烧?其實也很簡單杏愤,多線程可能會吧;

//getSingleton2 -4 假設解析

beforeSingletonCreation(beanName);

這段代碼就比較重要了已脓,關于上面說那個正在創(chuàng)建和正在銷毀的集合珊楼;這段代碼就能解釋,所以如果上面你沒看明白那個集合的意義度液,筆者這里用spring源碼來說明一下厕宗;先看看當代碼執(zhí)行到這里的時候語境



當spring覺得可以著手來創(chuàng)建bean的時候首先便是調(diào)用beforeSingletonCreation(beanName);判斷當前正在實例化的bean是否存在正在創(chuàng)建的集合當中,說白了就是判斷當前是否正在被創(chuàng)建堕担;因為spring不管創(chuàng)建原型bean還是單例bean已慢,當他需要正式創(chuàng)建bean的時候他會記錄一下這個bean正在創(chuàng)建(add到一個set集合當中);故而當他正式創(chuàng)建之前他要去看看這個bean有沒有正在被創(chuàng)建(是否存在集合當中); 為什么spring要去判斷是否存在這個集合呢霹购?原因很多除了你們能想到了(你們能想到的基本不會出現(xiàn)佑惠,比如并發(fā)啊,重復創(chuàng)建什么的齐疙,因為他已經(jīng)做了嚴格并發(fā)處理)膜楷,其實這個集合主要是為了循環(huán)依賴服務的,怎么服務的呢贞奋?慢慢看吧把将,首先我們來看下這行 代碼的具體內(nèi)容

    /**
     * Callback before singleton creation.
     * <p>The default implementation register the singleton as currently in creation.
     * @param beanName the name of the singleton about to be created
     * @see #isSingletonCurrentlyInCreation
     */
    protected void beforeSingletonCreation(String beanName) {
        if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }
    }

1、this.inCreationCheckExclusions.contains(beanName)這里是判斷當前需要創(chuàng)建的bean是否在Exclusions集合忆矛,被排除的bean,程序員可以提供一些bean不被spring初始化(哪怕被掃描到了请垛,也不初始化)催训,那么這些提供的bean便會存在這個集合當中;一般情況下我們不會提供宗收,而且與循環(huán)依賴無關漫拭;故而所以這里不做深入分析,后面文章如果寫到做分析混稽;

this.singletonsCurrentlyInCreation.add(beanName)采驻,如果當前bean不在排除的集合當中那么則這個bean添加到singletonsCurrentlyInCreation(當然這里只是把bean名字添加到集合审胚,為了方便我們直接認為把bean添加到集合吧,因為他能根據(jù)名字能找打?qū)腷ean)礼旅;

關于singletonsCurrentlyInCreation的定義參考下圖


其實就是一個set集合膳叨,當運行完this.singletonsCurrentlyInCreation.add(beanName) 之后結(jié)果大概如下圖這樣



我們可以通過debug來調(diào)試證明一下上面這幅圖



結(jié)果分析:當代碼運行完this.singletonsCurrentlyInCreation.add(beanName)之后可以看到singletonsCurrentlyInCreation集合當中只存在一個x,并且后天并沒有執(zhí)行x的構(gòu)造方法痘系,說明spring僅僅是把x添加到正在創(chuàng)建的集合當中菲嘴,但是并沒有完成bean的創(chuàng)建(因為連構(gòu)造方法都沒調(diào)用);

請一定注意這個集合的數(shù)據(jù)情況(目前只有一個x);因為這和循環(huán)依賴有天大的關系汰翠;add完x之后代碼接著往下執(zhí)行龄坪;

//getSingleton2 -5 開始分析

singletonObject = singletonFactory.getObject();
可能有讀者已經(jīng)忘記了singletonFactory這個對象怎么來的了;筆者再把代碼貼一遍吧

ObjectFactory<?>  singletonFactory = new ObjectFactory(){
    public Object getObject(){
        //其實這是個抽象類复唤,不能實例化
        //createBean是子類實現(xiàn)的健田,這里就不關心了
        //你就理解這不是一個抽象類吧
        AbstractBeanFactory abf = new AbstractBeanFactory();
        Object bean = abf.createBean(beanName, mbd, args);
        return bean;
    };
};
//傳入 beanName 和singletonFactory 對象
sharedInstance = getSingleton(beanName,singletonFactory);

singletonFactory.getObject();調(diào)用的就是上面代碼中getObject方法,換言之調(diào)用的是abf.createBean(beanName, mbd, args)佛纫;把創(chuàng)建好的bean返回出來妓局;至此第二次getSingleton方法結(jié)束,bean通過singletonFactory.getObject();調(diào)用createBean建完成雳旅;接下來分析createBean的源碼跟磨,繼續(xù)探討循環(huán)依賴的原理
AbstractAutowireCapableBeanFactory#createBean()方法中調(diào)用了doCreateBean方法創(chuàng)建bean;下圖是dubug流程



結(jié)果分析:因為執(zhí)行完doCreateBean之后X和Y的構(gòu)造方法都已經(jīng)完成了調(diào)用攒盈,說明這個方法里面對X做了實例化抵拘,也就是把bean創(chuàng)建好了,而且完成了循環(huán)依賴(因為Y的構(gòu)造方法也打印說明X在完成屬性注入的時候注入了Y型豁,所以Y也實例化了僵蛛,Y bean也創(chuàng)建好了);接下來重點分析這個doCreateBean方法內(nèi)容迎变。

我先給出這個方法的源碼全貌充尉;重點我用紅色標記了,并且會在進行代碼解析衣形;黃色線下面的讀者可以不用管驼侠,和本文內(nèi)容沒多大關系;

讀者可以好好看看下圖:方便你閱讀下面的代碼解析


//doCreateBean -1

instanceWrapper = createBeanInstance(beanName, mbd, args);

![20200319172652544.gif](https://upload-images.jianshu.io/upload_images/19275368-c57281d6353f09a9.gif?imageMogr2/auto-orient/strip) ![20200319172652544.gif](https://upload-images.jianshu.io/upload_images/19275368-6cc2b10b14e995ef.gif?imageMogr2/auto-orient/strip)

createBeanInstance 顧名思義就是創(chuàng)建一個實例谆吴,注意這里僅僅是創(chuàng)建一個實例對象倒源,還不能稱為bean;因為我文章一開頭就解釋了什么是bean句狼,什么是對象笋熬;好吧再啰嗦一下吧,文章比較長腻菇,不方便翻閱胳螟;

1昔馋、spring bean——受spring容器管理的對象,可能經(jīng)過了完整的spring bean生命周期(為什么是可能糖耸?難道還有bean是沒有經(jīng)過bean生命周期的秘遏?答案是有的,具體我們后面文章分析)蔬捷,最終存在spring容器當中垄提;一個bean一定是個對象
2、對象——任何符合java語法規(guī)則實例化出來的對象周拐,但是一個對象并不一定是spring bean铡俐;

同樣用dubug來說明一下:


運行完createBeanInstance之后控制打印了X構(gòu)造方法的內(nèi)容,說明X對象已經(jīng)被創(chuàng)建了妥粟,但是這個時候的x不是bean审丘,因為bean的生命周期才剛剛開始;這就好比你跑到天上人間勾给,問了各種你想問的問題之后交了1000塊錢滩报,但是這個時候你僅僅是個消費者,還不是渣男播急,因為一條龍的服務是從交錢開始脓钾,接下來的各種服務完成你才是一個名副其實的渣男,不知道這么解釋有沒有偏差桩警;為了把前面知識串起來可训,照例畫一下當前代碼的語境吧


這個createBeanInstance方法是如何把對象創(chuàng)建出來的呢?對應文章開頭說的bean的生命周期一共17步捶枢,其中的第8步(推斷構(gòu)造方法)和第9步(利用構(gòu)造方法反射來實例化對象)嗤形;具體如何推斷構(gòu)造方法我會在后面的博客分析吝镣;這里截個圖看看代碼就行氮帐,不做分析铲咨;

推斷構(gòu)造方法的代碼運行結(jié)果分析——注意這張圖比較長,讀者可以多看幾遍蒜鸡;因為推斷構(gòu)造方法筆者以為是屬于spring源碼中特別重要和特別難的一塊知識胯努;后面會有單獨博客來分析,所以讀者可以先多看看這張圖逢防;


至此x對象已經(jīng)實例化出來康聂,代碼往下執(zhí)行到合并beanDefinition,看圖吧

image.png

但是其實合并beanDefinition和本文討論的循環(huán)依賴無關胞四,故而先跳過;

//doCreateBean-2 開始解析

boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
        isSingletonCurrentlyInCreation(beanName));

這段代碼其實比較簡單伶椿,就是給earlySingletonExposure這個布爾類型的變量賦值辜伟;這個變量的意義是——是否支持(開啟了)循環(huán)依賴氓侧;如果返回true則spring會做一些特殊的操作來完成循環(huán)依賴;如果返回false导狡,則不會有特殊操作约巷;
那么這個布爾變量的賦值邏輯是怎樣的呢?上面代碼可知三個條件做&&運算旱捧,同時成立才會返回true独郎;
1、mbd.isSingleton()枚赡;判斷當前實例化的bean是否為單例氓癌;再一次說明原型是不支持循環(huán)依賴的;因為如果是原型這里就會返回false贫橙,由于是&&運算贪婉,整個結(jié)果都為false;在本文環(huán)境里X是默認單例的卢肃,故而整個條件是true疲迂。
2、this.allowCircularReferences莫湘;整個全局變量spring 默認為true尤蒿;當然spring提供了api供程序員修改,這個在本文開頭筆者解釋過(筆者是通過修改spring源碼來改變這個值為false)幅垮,在沒有修改的情況下這里也返回true
3腰池、isSingletonCurrentlyInCreation(beanName);判斷當前正在創(chuàng)建的bean是否在正在創(chuàng)建bean的集合當中军洼;還記得前文筆者已經(jīng)解釋過singletonsCurrentlyInCreation這個集合現(xiàn)在里面存在且只有一個x巩螃;故而也會返回true;
其實這三種情況需要關心的只有第二種匕争;因為第一種是否單例一般都是成立的避乏,因為如果是原型的循環(huán)依賴前面代碼已經(jīng)報錯了;壓根不會執(zhí)行到這里甘桑;第三種情況也一般是成立拍皮,因為這個集合是spring操作的,沒有提供api給程序員去操作跑杭;而正常流程下代碼執(zhí)行到這里铆帽,當前正在創(chuàng)建的bean是一定在那個集合里面的;換句話說這三個條件1和3基本恒成立德谅;唯有第二種情況可能會不成立爹橱,因為程序員可以通過api來修改第二個條件的結(jié)果;
總結(jié):spring的循環(huán)依賴窄做,不支持原型愧驱,不支持構(gòu)造方法注入的bean慰技;默認情況下單例bean是支持循環(huán)依賴的,但是也支持關閉组砚,關閉的原理就是設置allowCircularReferences=false吻商;spring提供了api來設置這個值;
至此我們知道boolean earlySingletonExposure=true糟红,那么代碼接著往下執(zhí)行艾帐;判斷這個變量;


if成立盆偿,進入分支柒爸;

//doCreateBean-3 開始分析

addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
這段代碼又用了lamda表達式;筆者為了初學者看懂陈肛,還是改成傳統(tǒng)代碼
ObjectFactory<?> singletonFactory = new ObjectFactory<?>(){
    public T getObject(){
        //至于這個getEarlyBeanReference方法的代碼揍鸟,后面再來說
        // 現(xiàn)在可以理解為就是返回 bean
        getEarlyBeanReference(beanName, mbd, bean);
        //getEarlyBeanReference 的代碼稍微復雜一點,可以簡單理解為下面這樣
        getEarlyBeanReference(beanName, mbd, bean){
            return bean;
        }
    }
}

也就是singletonFactory.getObject();其實就是返回當前正在實例化的bean
改完之后的代碼可以理解成這樣:
addSingletonFactory(beanName,singletonFactory);

addSingletonFactory(beanName,singletonFactory);顧名思義添加一個單例工廠句旱;其實這里要非常注意阳藻,因為大部分資料里面在說到spring循環(huán)依賴的時候都說是提前暴露一個半成品bean;筆者覺得這個不嚴格谈撒;甚至算錯誤了腥泥,所謂的提前暴露就是這里的add,但是我們看到源碼并不是add一個bean的啃匿,而是add一個工廠對象——singletonFactory蛔外;兩種說法有什么區(qū)別呢?區(qū)別可大了溯乒,簡直天壤之別夹厌;我們慢慢分析;這里bean和工廠有什么區(qū)別呢裆悄?在當前的語境下面bean就是x對象經(jīng)歷完spring生命周期之后矛纹;所謂的半成品bean,可能還沒有經(jīng)歷完整的生命周期光稼;而工廠對象呢或南?如果你去ObjectFactory的源碼或者直接顧名思義他是一個能夠產(chǎn)生對象的工廠,或者叫能夠產(chǎn)生bean的工廠艾君;換句話說bean是一個產(chǎn)品采够,而工廠是產(chǎn)生這些產(chǎn)品的公司;如果還不能理解換成天上人間可能好理解——冰火和全套的區(qū)別冰垄,冰火是全套里面的一個項目蹬癌,除了冰火還有其他項目;

那么spring在這里add的是singletonFactory這個工廠對象(這個工廠可以產(chǎn)生半成品對象),而不是一個半成品對象逝薪;相當于這里add的是全套伴奥,而不是冰火;將來拿出來的時候是得到工廠翼闽,繼而通過工廠得到半成品bean;將來拿出來的是全套洲炊,你可以在全套里面肆意選擇一個項目感局;不知道我又沒有解釋清楚這個問題;

當然說了這么多可能你還是沒明白為什么需要在這里add這個工廠對象呢暂衡?還有add到哪里去呢询微?

讀者可以好好看看上圖,筆者在spring源碼當中把注釋寫上了(注釋的信息很重要狂巢,認真看看)撑毛,整個方法其實就是對三個map操作,至于這三個map的意義唧领,參考下圖

通過代碼可以得知singletonFactory主要被add到二級緩存中藻雌;至于為什么要add到這個map?主要了循環(huán)依賴斩个,提前暴露這個工廠胯杭;當然如果你不理解為什么要提前暴露,沒關系往下看受啥,看完文章一定會知道的做个;

保持好習慣照例畫個圖,讓讀者知道現(xiàn)在的情況吧



當然這里還是用一幅圖來秒殺一下這個三個map的各種情況吧



一級緩存:可能存在很多bean滚局,比如spring各種內(nèi)置bean居暖,比如你項目里面其他的已經(jīng)創(chuàng)建好的bean,但是在X的創(chuàng)建過程中藤肢,一級緩存中絕對是沒有xbean的太闺,也沒用y;因為spring創(chuàng)建bean默認的順序是根據(jù)字母順序的谤草;

二級緩存:里面現(xiàn)在僅僅存在一個工廠對象跟束,對應的key為x的beanName,并且這個bean工廠對象的getObect方法能返回現(xiàn)在的這個時候的x(半成品的xbean)
put完成之后丑孩,代碼接著往下執(zhí)行冀宴;

三級緩存:姑且認為里面什么都沒有吧

//doCreateBean-4 開始解析

populateBean(beanName, mbd, instanceWrapper);

populateBean這個方法可謂大名鼎鼎,主要就是完成屬性注入温学,也就是大家常常說的自動注入略贮;假設本文環(huán)境中的代碼運行完這行代碼那么則會注入y,而y又引用了x,所以注入進來的y對象逃延,也完成了x的注入览妖;什么意思呢?首先看一下沒有執(zhí)行populateBean之前的情況

沒有執(zhí)行populateBean之前只實例化了X揽祥,Y并沒實例化讽膏,那么Y也不能注入了;接下來看看執(zhí)行完這行代碼之后的情況

populateBean里面的代碼以后我更新文章來說明拄丰,本文先來猜測一下這個方法里面究竟干了什么事府树;
x 填充 y (簡稱 xpy)首先肯定需要獲取y,調(diào)用getBean(y)料按,getBean的本質(zhì)上文已經(jīng)分析過貨進入到第一次調(diào)用getSingleton奄侠,讀者可以回顧一下上文我對doGetBean方法名字的解釋里說了這個方法是創(chuàng)建bean和獲取共用的;

第一次getSingleton會從單例池獲取一下y载矿,如果y沒有存在單例池則開始創(chuàng)建y垄潮;

創(chuàng)建y的流程和創(chuàng)建x一模一樣,都會走bean的生命周期闷盔;比如把y添加到正在創(chuàng)建的bean的集合當中弯洗,推斷構(gòu)造方法,實例化y馁筐,提前暴露工廠對象(二級緩存里面現(xiàn)在有兩個工廠了涂召,分別是x和y)等等。敏沉。果正。。重復x的步驟盟迟;

直到y(tǒng)的生命周期走到填充x的時候ypx,第一次調(diào)用getSingletion獲取x秋泳?這里問個問題,能否獲取到x呢攒菠?

在回答這個問題之前我們先把該畫的圖貼出來迫皱,首先那個正在被創(chuàng)建bean的集合已經(jīng)不在是只有一個x了;(讀者可以對比一下上文的圖)

image.png

然后我們再把xpy到y(tǒng)px的流程圖貼出來辖众,請讀者仔細看看

是否能夠獲取到x呢卓起?首先我們想如果獲取失敗則又要創(chuàng)建x—>實例化x—填充屬性----獲取y--------。凹炸。戏阅。。啤它。奕筐。舱痘。就無限循環(huán)了;所以結(jié)果是完成了循環(huán)依賴离赫,那么這里肯定能夠獲取到x芭逝;那么獲取到x后流程是怎樣呢?



那么為什么能夠獲取到x呢渊胸?講道理聯(lián)系上文第一次調(diào)用getSingleton是無法獲取到x的旬盯?因為我們上面說過第一次調(diào)用getSingleton是從單例池當中獲取一個bean,但是x顯然沒有完成生命周期(x只走到了填充y翎猛,還有很多生命周期沒走完),所以應該是獲取不到的办成?為了搞清楚這個原因得去查看第一次getSingleton的源碼搂漠;如果讀者有留意的話筆者前面只是憑只管告訴你第一次getSingleton是從單例池當中獲取一個bean怔毛,并沒有去證明,也就是沒有去分析第一次getSingleton的源碼抗果;而且我在總結(jié)第一次getSingleton的時候用了目前這個詞筋帖;
顯然這是筆者前面故意挖的坑,所以各位讀者在閱讀別人的文章或者書籍的時候一定要小心驗證冤馏;包括筆者的文章如果有錯誤一定記得告訴我日麸;

下面來開始對第一次getSIngleton源碼做深入分析;首先把源碼以及我寫的注釋貼出來逮光,分為圖片和源代碼代箭,建議大家看圖片,可讀性好

源碼:如果你仔細看了上面的圖片可以跳過這里的源碼展示

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        //從單例池當(一級緩存)中直接拿涕刚,也就是文章里面'目前'的解釋
        //這也是為什么getBean("xx")能獲取一個初始化好bean的根本代碼
        Object singletonObject = this.singletonObjects.get(beanName);
        //如果這個時候是x注入y嗡综,創(chuàng)建y,y注入x副女,獲取x的時候那么x不在容器
        //第一個singletonObject == null成立
        //第二個條件判斷是否存在正在創(chuàng)建bean的集合當中蛤高,前面我們分析過蚣旱,成立
        //進入if分支
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            synchronized (this.singletonObjects) {
                //先從三級緩存那x?為什么先從三級緩存拿戴陡?下文解釋
                singletonObject = this.earlySingletonObjects.get(beanName);
                //講道理是拿不到的塞绿,因為這三個map現(xiàn)在只有二級緩存中存了一個工廠對象
                //回顧一下文章上面的流程講工廠對象那里,把他存到了二級緩存
                //所以三級緩存拿到的singletonObject==null  第一個條件成立
                //第二個條件allowEarlyReference=true恤批,這個前文有解釋
                //就是spring循環(huán)依賴的開關异吻,默認為true 進入if分支
                if (singletonObject == null && allowEarlyReference) {
                    //從二級緩存中獲取一個 singletonFactory,回顧前文喜庞,能獲取到
                    //由于這里的beanName=x诀浪,故而獲取出來的工廠對象,能產(chǎn)生一個x半成品bean
                    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                    //由于獲取到了延都,進入if分支
                    if (singletonFactory != null) {
                        //調(diào)用工廠對象的getObject()方法雷猪,產(chǎn)生一個x的半成品bean
                        //怎么產(chǎn)生的?下文解釋晰房,比較復雜
                        singletonObject = singletonFactory.getObject();
                        //拿到了半成品的xbean之后求摇,把他放到三級緩存;為什么殊者?下文解釋
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        //然后從二級緩存清除掉x的工廠對象与境;?為什么猖吴,下文解釋
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }

針對上面的源碼我做一個簡單的總結(jié):首先spring從單例池當中獲取x摔刁,前面說過獲取不到,然后判斷是否在正在創(chuàng)建bean的集合當中海蔽,前面分析過這個集合現(xiàn)在存在x共屈,和y;所以if成立進入分支党窜;進入分支spring直接從三級緩存中獲取x趁俊,根據(jù)前面的分析三級緩存當中現(xiàn)在什么都沒有,故而返回nll刑然;進入下一個if分支寺擂,從二級緩存中獲取一個ObjectFactory工廠對象;根據(jù)前面分析泼掠,二級緩存中存在x怔软,故而可以獲取到;跟著調(diào)用singletonFactory.getObject();拿到一個半成品的x bean對象择镇;然后把這個x對象放到三級緩存挡逼,同時把二級緩存中x清除(此時二級緩存中只存在一個y了,而三級緩存中多了一個x)腻豌;

問題1家坎、為什么首先是從三級緩存中取呢嘱能?主要是為了性能,因為三級緩存中存的是一個x對象虱疏,如果能取到則不去二級找了惹骂;哪有人會問二級有什么用呢?為什么一開始要存工廠呢做瞪?為什么一開始不直接存三級緩存对粪?這里稍微有點復雜,如果直接存到三級緩存装蓬,只能存一個對象著拭,假設以前存這個對象的時候這對象的狀態(tài)為xa,但是我們這里y要注入的x為xc狀態(tài)牍帚,那么則無法滿足儡遮;但是如果存一個工廠,工廠根據(jù)情況產(chǎn)生任意xa或者xb或者xc等等情況暗赶;比如說aop的情況下x注入y峦萎,y也注入x;而y中注入的x需要加代理(aop)忆首,但是加代理的邏輯在注入屬性之后,也就是x的生命周期周到注入屬性的時候x還不是一個代理對象被环,那么這個時候把x存起來糙及,然后注入y,獲取筛欢、創(chuàng)建y浸锨,y注入x,獲取x版姑;拿出來的x是一個沒有代理的對象柱搜;但是如果存的是個工廠就不一樣;首先把一個能產(chǎn)生x的工廠存起來剥险,然后注入y聪蘸,注入y的時候獲取、創(chuàng)建y表制,y注入x健爬,獲取x,先從三級緩存獲取么介,為null娜遵,然后從二級緩存拿到一個工廠,調(diào)用工廠的getObject()壤短;spring在getObject方法中判斷這個時候x被aop配置了故而需要返回一個代理的x出來注入給y设拟。當然有的讀者會問你不是前面說過getObject會返回一個當前狀態(tài)的xbean嘛慨仿?我說這個的前提是不去計較getObject的具體源碼,因為這塊東西比較復雜纳胧,需要去了解spring的后置處理器功能镰吆,這里先不討論,總之getObject會根據(jù)情況返回一個x躲雅,但是這個x是什么狀態(tài)鼎姊,spring會自己根據(jù)情況返回;

問題2相赁、為什么要從二級緩存remove相寇?因為如果存在比較復雜的循環(huán)依賴可以提高性能;比如x钮科,y唤衫,z相互循環(huán)依賴,那么第一次y注入x的時候從二級緩存通過工廠返回了一個x绵脯,放到了三級緩存佳励,而第二次z注入x的時候便不需要再通過工廠去獲得x對象了。因為if分支里面首先是訪問三級緩存蛆挫;至于remove則是為了gc吧赃承;

至此循環(huán)依賴的內(nèi)容講完,有錯誤歡迎指正悴侵,歡迎留言提問瞧剖;如果覺得筆者寫的對你有幫助可以多多點贊轉(zhuǎn)發(fā)吧;

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末可免,一起剝皮案震驚了整個濱河市抓于,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌浇借,老刑警劉巖捉撮,帶你破解...
    沈念sama閱讀 221,695評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異妇垢,居然都是意外死亡巾遭,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評論 3 399
  • 文/潘曉璐 我一進店門闯估,熙熙樓的掌柜王于貴愁眉苦臉地迎上來恢总,“玉大人,你說我怎么就攤上這事睬愤∑拢” “怎么了?”我有些...
    開封第一講書人閱讀 168,130評論 0 360
  • 文/不壞的土叔 我叫張陵尤辱,是天一觀的道長砂豌。 經(jīng)常有香客問我厢岂,道長,這世上最難降的妖魔是什么阳距? 我笑而不...
    開封第一講書人閱讀 59,648評論 1 297
  • 正文 為了忘掉前任塔粒,我火速辦了婚禮,結(jié)果婚禮上筐摘,老公的妹妹穿的比我還像新娘卒茬。我一直安慰自己,他們只是感情好咖熟,可當我...
    茶點故事閱讀 68,655評論 6 397
  • 文/花漫 我一把揭開白布圃酵。 她就那樣靜靜地躺著,像睡著了一般馍管。 火紅的嫁衣襯著肌膚如雪郭赐。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,268評論 1 309
  • 那天确沸,我揣著相機與錄音捌锭,去河邊找鬼。 笑死罗捎,一個胖子當著我的面吹牛观谦,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播桨菜,決...
    沈念sama閱讀 40,835評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼豁状,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了雷激?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,740評論 0 276
  • 序言:老撾萬榮一對情侶失蹤告私,失蹤者是張志新(化名)和其女友劉穎屎暇,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體驻粟,經(jīng)...
    沈念sama閱讀 46,286評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡根悼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,375評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了蜀撑。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片挤巡。...
    茶點故事閱讀 40,505評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖酷麦,靈堂內(nèi)的尸體忽然破棺而出矿卑,到底是詐尸還是另有隱情,我是刑警寧澤沃饶,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布母廷,位于F島的核電站轻黑,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏琴昆。R本人自食惡果不足惜氓鄙,卻給世界環(huán)境...
    茶點故事閱讀 41,873評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望业舍。 院中可真熱鬧抖拦,春花似錦、人聲如沸舷暮。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽脚牍。三九已至向臀,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間诸狭,已是汗流浹背券膀。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留驯遇,地道東北人芹彬。 一個月前我還...
    沈念sama閱讀 48,921評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像叉庐,于是被迫代替她去往敵國和親舒帮。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,515評論 2 359