Spring三級(jí)緩存&循環(huán)依賴

循環(huán)依賴

@Component
public class Depend1 {
    @Autowired
    private Depend2 d;

    public Depend1(){}
}

@Component
public class Depend2 {
    @Autowired
    private Depend1 d;

    public Depend2(){}
}
假設(shè)Depend1先被實(shí)例化脊凰,當(dāng)屬性填充時(shí)腕让,其中一個(gè)屬性依賴Depend2沟优,就會(huì)跳至實(shí)例化Depend2隆嗅,當(dāng)Depend2屬性填充時(shí)功舀,又依賴Depend1棺棵,構(gòu)成了循環(huán)依賴谒撼。

通過(guò)set方法構(gòu)成循環(huán)依賴可以通過(guò)三級(jí)緩存解決食寡,但是通過(guò)構(gòu)造函數(shù)構(gòu)成的循環(huán)依賴會(huì)報(bào)錯(cuò)的


流程

AbstractBeanFactory的doGetBean創(chuàng)建bean時(shí),
做兩件事:
1廓潜、getSingleton(beanName)先從緩存中獲得已經(jīng)注冊(cè)的代理對(duì)象抵皱。通常獲得為null

2、如果沒(méi)有被注冊(cè)辩蛋,并且是單例時(shí)呻畸,getSingleton(beanName,ObjectFactory)創(chuàng)建對(duì)象,ObjectFactory定義了創(chuàng)建bean的具體方法

    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;

        // Eagerly check singleton cache for manually registered singletons.
        Object sharedInstance = getSingleton(beanName);
              if (sharedInstance != null && args == null) {

               }else{
                if (mbd.isSingleton()) {
                    sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
                        @Override
                        public Object getObject() throws BeansException {
                            try {
                                return createBean(beanName, mbd, args);
                            }
                        }
                    });
                    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
                }
       }

getSingleton(beanName,ObjectFactory)創(chuàng)建bean時(shí)
做兩件事:
1堪澎、通過(guò)傳入的ObjectFactory創(chuàng)建對(duì)象擂错,
2、finally中addSingleton(beanName,singletonObject) 將創(chuàng)建的bean加入緩存
此時(shí)bean已經(jīng)完成實(shí)例創(chuàng)建樱蛤、屬性注入钮呀、init方法執(zhí)行

    public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
        synchronized (this.singletonObjects) {
            Object singletonObject = this.singletonObjects.get(beanName);
            if (singletonObject == null) {
                boolean newSingleton = false;
                try {
                    singletonObject = singletonFactory.getObject();
                    newSingleton = true;
                }
                finally {
                if (newSingleton) {
                    addSingleton(beanName, singletonObject);
                }
            }
            return (singletonObject != NULL_OBJECT ? singletonObject : null);
        }
    }

上面的singletonFactory.getObject()實(shí)際調(diào)用的是doCreateBean

主要做
1、createBaenInstance()昨凡,會(huì)根據(jù)無(wú)參構(gòu)造器爽醋、工廠方法創(chuàng)建bean實(shí)例(還沒(méi)有依賴注入)
2、addSingletonFactory()將創(chuàng)建的bean放入緩存
注意:ObjectFactory中的getEarlyBeanRefernce會(huì)根據(jù)bean得到其代理對(duì)象
3便脊、之后是屬性注入
4蚂四、init方法執(zhí)行

    protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
        // Instantiate the bean.
        BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args);
        final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
        Class<?> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);

        // Eagerly cache singletons to be able to resolve circular references
        // even when triggered by lifecycle interfaces like BeanFactoryAware.
        boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
                isSingletonCurrentlyInCreation(beanName));
        if (earlySingletonExposure) {
            addSingletonFactory(beanName, new ObjectFactory<Object>() {
                @Override
                public Object getObject() throws BeansException {
                    return getEarlyBeanReference(beanName, mbd, bean);
                }
            });
        }

        // Initialize the bean instance.
        Object exposedObject = bean;
        try {
            populateBean(beanName, mbd, instanceWrapper);
            if (exposedObject != null) {
                exposedObject = initializeBean(beanName, exposedObject, mbd);
            }
        }
        return exposedObject;
    }

可以看到bean對(duì)象實(shí)例化后屬性注入前會(huì)放入一次緩存,屬性注入等完成后會(huì)再次放入緩存哪痰。 每次獲得bean對(duì)象時(shí)先從緩存中撈取

下面看下注入屬性前放入緩存的操作
1遂赠、放入三級(jí)緩存,存的實(shí)際是獲得bean代理對(duì)象的方法晌杰,而不是bean對(duì)象
2跷睦、確保二級(jí)緩存中沒(méi)有

    /**一級(jí)緩存 Cache of singleton objects: bean name --> bean instance */
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);

    /**二級(jí)緩存 Cache of singleton factories: bean name --> ObjectFactory */
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);

    /**三級(jí)緩存 Cache of early singleton objects: bean name --> bean instance */
    private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);

    /** Set of registered singletons, containing the bean names in registration order */
    private final Set<String> registeredSingletons = new LinkedHashSet<String>(256);
        // ObjectFactory創(chuàng)建的是bean對(duì)象的代理對(duì)象
    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);
            }
        }
    }

對(duì)象完全創(chuàng)建好后,再次放入緩存的邏輯:
1肋演、放入一級(jí)緩存singletonObjects抑诸,存儲(chǔ)的是bean對(duì)象的代理對(duì)象
2烂琴、保證二級(jí)緩存中沒(méi)有該bean
3、保證三級(jí)緩存中沒(méi)有該bean

    /**一級(jí)緩存 Cache of singleton objects: bean name --> bean instance */
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);

    /**二級(jí)緩存 Cache of singleton factories: bean name --> ObjectFactory */
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);

    /**三級(jí)緩存 Cache of early singleton objects: bean name --> bean instance */
    private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);

    /** Set of registered singletons, containing the bean names in registration order */
    private final Set<String> registeredSingletons = new LinkedHashSet<String>(256);

    protected void addSingleton(String beanName, Object singletonObject) {
        synchronized (this.singletonObjects) {
            this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));
            this.singletonFactories.remove(beanName);
            this.earlySingletonObjects.remove(beanName);
            this.registeredSingletons.add(beanName);
        }
    }

DefaultSingletonBeanRegistry
讀緩存時(shí):
1蜕乡、先從二級(jí)緩存中撈取
2奸绷、否則,從三級(jí)緩存中拿取獲得代理對(duì)象的方法层玲,并生成代理對(duì)象
3号醉、將代理對(duì)象放入二級(jí)緩存
4、移除三級(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 != NULL_OBJECT ? singletonObject : null);
    }

為什么要三級(jí)緩存

A.沒(méi)有循環(huán)依賴
1称簿、讀緩存:
①二級(jí)緩存中沒(méi)有
②三級(jí)緩存中沒(méi)有
2扣癣、通過(guò)BeanFactory創(chuàng)建bean后第一次放入緩存
①放入三級(jí)緩存
②一、二級(jí)緩存中清除
3憨降、屬性注入
4父虑、init
執(zhí)行BeanPostProcessor時(shí)會(huì)創(chuàng)建bean的代理對(duì)象
5、第二次放入緩存
①將代理對(duì)象放入一級(jí)緩存
②二三級(jí)緩存清除

B.循環(huán)依賴
1授药、Depend1讀緩存沒(méi)有
2士嚎、通過(guò)BeanFactory創(chuàng)建bean后第一次放入緩存
3、屬性注入時(shí)發(fā)現(xiàn)依賴Depend2悔叽,開(kāi)始創(chuàng)建Depend2對(duì)象
4莱衩、從緩存中讀Depend2,沒(méi)有
5娇澎、通過(guò)BeanFactory創(chuàng)建Depend2后第一次放入緩存
6笨蚁、屬性注入時(shí)發(fā)現(xiàn)依賴Depend1,開(kāi)始創(chuàng)建Depend1
7趟庄、從緩存中讀取Depend1
①二級(jí)緩存中沒(méi)有
②三級(jí)緩存中獲得BeanFactory創(chuàng)建代理對(duì)象
8括细、Depend2獲得Depend1的代理對(duì)象,完成屬性注入
9戚啥、完成Depend2創(chuàng)建
10奋单、Depend1獲得Depend2對(duì)象,完成創(chuàng)建

不考慮代理對(duì)象猫十,實(shí)際用二級(jí)緩存就可以解決循環(huán)依賴览濒,只需要?jiǎng)?chuàng)建實(shí)例后屬性注入前,將實(shí)例存入二級(jí)緩存拖云,循環(huán)依賴時(shí)直接從二級(jí)緩存中獲得bean對(duì)象

但是對(duì)外只能暴露bean對(duì)象的代理對(duì)象時(shí)贷笛,只能用三級(jí)緩存:
如果二級(jí)緩存中存儲(chǔ)bean對(duì)象,注入時(shí)注入的就不是代理對(duì)象
如果二級(jí)緩存中存儲(chǔ)創(chuàng)建bean對(duì)象的代理對(duì)象的BeanFactory宙项,則每次獲得的不是同一個(gè)代理對(duì)象

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末昨忆,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子杉允,更是在濱河造成了極大的恐慌邑贴,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,204評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件叔磷,死亡現(xiàn)場(chǎng)離奇詭異拢驾,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)改基,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門繁疤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人秕狰,你說(shuō)我怎么就攤上這事稠腊。” “怎么了鸣哀?”我有些...
    開(kāi)封第一講書人閱讀 164,548評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵架忌,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我我衬,道長(zhǎng)叹放,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 58,657評(píng)論 1 293
  • 正文 為了忘掉前任挠羔,我火速辦了婚禮井仰,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘破加。我一直安慰自己俱恶,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布范舀。 她就那樣靜靜地躺著合是,像睡著了一般。 火紅的嫁衣襯著肌膚如雪尿背。 梳的紋絲不亂的頭發(fā)上端仰,一...
    開(kāi)封第一講書人閱讀 51,554評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音田藐,去河邊找鬼荔烧。 笑死,一個(gè)胖子當(dāng)著我的面吹牛汽久,可吹牛的內(nèi)容都是我干的鹤竭。 我是一名探鬼主播,決...
    沈念sama閱讀 40,302評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼景醇,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼臀稚!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起三痰,我...
    開(kāi)封第一講書人閱讀 39,216評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤吧寺,失蹤者是張志新(化名)和其女友劉穎窜管,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體稚机,經(jīng)...
    沈念sama閱讀 45,661評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡幕帆,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了赖条。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片失乾。...
    茶點(diǎn)故事閱讀 39,977評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖纬乍,靈堂內(nèi)的尸體忽然破棺而出碱茁,到底是詐尸還是另有隱情,我是刑警寧澤仿贬,帶...
    沈念sama閱讀 35,697評(píng)論 5 347
  • 正文 年R本政府宣布纽竣,位于F島的核電站,受9級(jí)特大地震影響诅蝶,放射性物質(zhì)發(fā)生泄漏退个。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評(píng)論 3 330
  • 文/蒙蒙 一调炬、第九天 我趴在偏房一處隱蔽的房頂上張望语盈。 院中可真熱鬧,春花似錦缰泡、人聲如沸刀荒。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,898評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)缠借。三九已至,卻和暖如春宜猜,著一層夾襖步出監(jiān)牢的瞬間泼返,已是汗流浹背姐扮。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,019評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工仑乌, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人颈抚。 一個(gè)月前我還...
    沈念sama閱讀 48,138評(píng)論 3 370
  • 正文 我出身青樓叫乌,卻偏偏與公主長(zhǎng)得像柴罐,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子憨奸,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評(píng)論 2 355