spring依賴注入——循環(huán)依賴

上一篇博客簡單地分析了下依賴注入。但是對于依賴注入的很多細節(jié),都沒有深入的分析。這一篇博客會繼續(xù)分析spring的依賴注入。這篇博客會解決分析getBean緩存時候遺留下來的循環(huán)依賴問題。

循環(huán)依賴分析

首先明確下,只有單例情況下,spring才會試著去解決循環(huán)依賴問題,多例是不會去解決循環(huán)依賴的。這個也好理解,如果是多例的話,比如a -> b 并且 b -> a 那么,當A a=new A(); 之后要注入b,b卻是多例的,那么究竟該注入哪個B是不確定的。如下圖:


接下來我們分析,為啥會有循環(huán)依賴的問題。

先來分析沒有循環(huán)依賴的問題

public static class A{
    
    private B b;

    //省略get和set方法
}

public static class B{
}

這個時候,如果spring先初始化A,然后會發(fā)現(xiàn)A依賴于B,然后就會初始化B,最后注入到A里。

整個流程用代碼表示大概如下所示:

A a = 創(chuàng)建A
B b = 創(chuàng)建B
        -----> 創(chuàng)建A子流程  a.setB(b);

但是假設B也依賴了A呢?即

public static class B{
    
    private A a;

}

那樣依賴注入過程就會變成(簡單示例)

A a = 創(chuàng)建A
B b = 創(chuàng)建B
       ---->  創(chuàng)建B子流程 b.setA(????);  //  這個時候A還沒創(chuàng)建完成呢
a.setB(b);

那么如何解決呢?很簡單,把那個還沒創(chuàng)建完的A(只是new了,但是沒有進行依賴注入的A)set到B里就好了。

弄清了這個流程之后,我們再來分析spring是如何進行依賴注入的。

spring對引用類型的注入

這里我先從spring對引用屬性的注入開始。

<bean id="person" class="com.hdj.learn.spring.demo.Person">
    <property name="car" ref="car"></property>
</bean>

即ref的注入

private Object resolveReference(Object argName, RuntimeBeanReference ref) {
    try {
        String refName = ref.getBeanName();
        refName = String.valueOf(doEvaluate(refName));
        if (ref.isToParent()) {
            if (this.beanFactory.getParentBeanFactory() == null) {
                throw new BeanCreationException(
                        this.beanDefinition.getResourceDescription(), this.beanName,
                        "Can't resolve reference to bean '" + refName +
                        "' in parent factory: no parent factory available");
            }
            return this.beanFactory.getParentBeanFactory().getBean(refName);
        }
        else {
            Object bean = this.beanFactory.getBean(refName);
            this.beanFactory.registerDependentBean(refName, this.beanName);
            return bean;
        }
    }
    catch (BeansException ex) {
        throw new BeanCreationException(
                this.beanDefinition.getResourceDescription(), this.beanName,
                "Cannot resolve reference to bean '" + ref.getBeanName() + "' while setting " + argName, ex);
    }
}

實現(xiàn)很簡單,就是從beanFactory里獲取要依賴的對象

我們再來回顧下流程

問題是,當走到6時候,似乎又會回到1,這樣不就死循環(huán)了么?

重點就是,第六步的獲取A,我們回到doGetBean方法中。

getSingleton 方法

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    //已創(chuàng)建的對象里面找下
    Object singletonObject = this.singletonObjects.get(beanName);
    //沒找到,并且當前類正在被創(chuàng)建
    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);
}

我們在分析初始化bean的緩存部分時,曾分析過這幾個緩存。當時其實只知道了singletonObjects是存儲了已經(jīng)創(chuàng)建了的對象。

現(xiàn)在讓我們回頭再看看這些緩存。

首先看看singletonFactories賦值的地方。

doCreateBean

// 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) {
    if (logger.isDebugEnabled()) {
        logger.debug("Eagerly caching bean '" + beanName +
                "' to allow for resolving potential circular references");
    }
    addSingletonFactory(beanName, new ObjectFactory<Object>() {
        @Override
        public Object getObject() throws BeansException {
            //默認實現(xiàn)返回bean
            return getEarlyBeanReference(beanName, mbd, bean);
        }
    });
}

在創(chuàng)建bean時候,有這樣一段代碼,當需要進行提前暴露時候(當前創(chuàng)建對象單例 + 允許循環(huán)引用 + 當前類正在被創(chuàng)建)會調(diào)用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);
        }
    }
}

這里就碰見了我們要分析的singletonFactories,它存儲了beanName -> 此處的匿名內(nèi)部類singletonFactory。

singletonFactory

我們再回到getSingleton方法,以及之前繪制的圖

ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
    singletonObject = singletonFactory.getObject();
    this.earlySingletonObjects.put(beanName, singletonObject);
    this.singletonFactories.remove(beanName);
}

邏輯很簡單


三級緩存

我們經(jīng)常聽說spring通過三級緩存解決了循環(huán)依賴,其實三級緩存非常簡單。就是指我們分析過的

Map<String, Object> singletonObjects
Map<String, ObjectFactory<?>> singletonFactories
Map<String, Object> earlySingletonObjects

這里分別舉這樣三個例子。
A 依賴 B(B不依賴A)


一級緩存

A 依賴 B && B依賴A


三級緩存

A依賴B && B依賴A + B依賴C && C 依賴 A

二級緩存

至此所謂的三級緩存及其使用,應該就非常的清楚了。

總結(jié)下

這篇博客分析了spring依賴注入中,循環(huán)依賴的問題,分析了在spring初始化bean時,各級緩存的作用。應該算是挺清晰的了。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • 1.1 Spring IoC容器和bean簡介 本章介紹了Spring Framework實現(xiàn)的控制反轉(zhuǎn)(IoC)...
    起名真是難閱讀 2,674評論 0 8
  • 1.1 spring IoC容器和beans的簡介 Spring 框架的最核心基礎的功能是IoC(控制反轉(zhuǎn))容器,...
    simoscode閱讀 6,851評論 2 22
  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,654評論 19 139
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 47,281評論 6 342
  • 歲月的年輪在不停地轉(zhuǎn)動,小草隨風搖曳它的快樂,花兒與那些駐足風景的過客微笑致意,雙蝶迷戀花的清香,遲遲不肯離去...
    毓琳閱讀 260評論 0 0

友情鏈接更多精彩內(nèi)容