Spring循环依赖处理分析

循环依赖就是N个类中循环嵌套引用,如果在日常开发中我们用new 对象的方式发生这种循环依赖的话程序会在运行时一直循环调用,直至内存溢出报错,如下图所示:


循环依赖的情况:

  • 构造器参数依赖
  • setter方式singleton单例
  • setter方式prototype多例

1、构造器参数依赖

Spring容器会将每一个正在创建的Bean 标识符放在一个“当前创建Bean池”中,Bean标识符在创建过程中将一直保持在这个池中,因此如果在创建Bean过程中发现自己已经在“当前创建Bean池”里时将抛出
BeanCurrentlyInCreationException异常表示循环依赖;而对于创建完毕的Bean将从“当前创建Bean池”中清除掉。

public class StudentA {  
  
    private StudentB studentB ;  
  
    public void setStudentB(StudentB studentB) {  
        this.studentB = studentB;  
    }  
  
    public StudentA() {  
    }  
      
    public StudentA(StudentB studentB) {  
        this.studentB = studentB;  
    }  
} 
public class StudentB {  
  
    private StudentC studentC ;  
  
    public void setStudentC(StudentC studentC) {  
        this.studentC = studentC;  
    }  
      
    public StudentB() {  
    }  
  
    public StudentB(StudentC studentC) {  
        this.studentC = studentC;  
    }  
} 
public class StudentC {  
  
    private StudentA studentA ;  
  
    public void setStudentA(StudentA studentA) {  
        this.studentA = studentA;  
    }  
  
    public StudentC() {  
    }  
   
    public StudentC(StudentA studentA) {  
        this.studentA = studentA;  
    }  
}  

OK,上面是很基本的3个类,,StudentA有参构造是StudentB。StudentB的有参构造是StudentC,StudentC的有参构造是StudentA ,这样就产生了一个循环依赖的情况,我们都把这三个Bean交给Spring管理,并用有参构造实例化。

<bean id="a" class="com.zfx.student.StudentA">  
    <constructor-arg index="0" ref="b"></constructor-arg>  
</bean>  
<bean id="b" class="com.zfx.student.StudentB">  
    <constructor-arg index="0" ref="c"></constructor-arg>  
</bean>  
<bean id="c" class="com.zfx.student.StudentC">  
    <constructor-arg index="0" ref="a"></constructor-arg>  
</bean>

测试类:

public class Test {  
    public static void main(String[] args) {  
        ApplicationContext context = new ClassPathXmlApplicationContext("com/zfx/student/applicationContext.xml");  
        //System.out.println(context.getBean("a", StudentA.class));  
    }  
}  

执行结果报错:

Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException:   
    Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference? 

报错原因:
Spring容器先创建单例StudentA,StudentA依赖StudentB,然后将A放在“当前创建Bean池”中,此时创建StudentB,StudentB依赖StudentC ,然后将B放在“当前创建Bean池”中,此时创建StudentC,StudentC又依赖StudentA, 但是,此时Student已经在池中,所以会报错,,因为在池中的Bean都是未初始化完的,所以会依赖错误 ,(初始化完的Bean会从池中移除)

2、setter的singleton依赖

bean的生命周期如下图:


2.1缓存的定义

先看DefaultSingletonBeanRegistry关于缓存的定义

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

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

/** Cache of early singleton objects: bean name --> bean instance */
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
/** Names of beans that are currently in creation */
private final Set<String> singletonsCurrentlyInCreation =
            Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>(16));

功能介绍:

缓存 用途
singletonObjects 用于存放完全初始化好的 bean,从该缓存中取出的 bean 可以直接使用
earlySingletonObjects 存放原始的 bean 对象(尚未填充属性),用于解决循环依赖
singletonFactories 存放 bean 工厂对象,用于解决循环依赖

可以把singletonObjects 看做一级缓存,bean创建时默认会先从singletonObjects中取bean,并且在创建后直接将对象放入singletonObjects中缓存;earlySingletonObjects看做二级缓存,在bean创建时提前将ObjectFactory暴露给singletonFactories,这样就可以在对象创建完成之前也可以获得对象的引用。

2.2bean创建的过程

实例化流程图如下:


AbstarctBeanFactorygetBean开始,是个空壳方法,直接调用doGetBean(name, null, null, false),该方法的分析如下(只分析了比较重要部分的代码,其他地方有所省略):

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;

    // 在获取bean的时候优先从缓存中获取,对于新创建的对象,这里是无法获得值;对于依赖的对象,可能已经创建或者正在创建,
    //直接从缓存中获取,可以避免重复创建
    Object sharedInstance = getSingleton(beanName);①
    if (sharedInstance != null && args == null) {
        //判断你是否FactoryBean,如果是FactoryBean类型,则调用getObject方法注入的实际对象
        bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }

    else {
        // 对于作用域为prototype的bean,每次请求都会创建一个实例对象,
        // 如果正在创建或已经创建,则异常,原因为:对于prototype作用域的bean,
        // Spring不会进行缓存,因此无法提前暴露一个创建中的bean
        if (isPrototypeCurrentlyInCreation(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }

        ...

        try {
            final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);

            // 此处用于实例化bean的依赖,比如A依赖B,如果B还未实例化,则先实例化B;
            // 即调用getBean对B进行实例化
            String[] dependsOn = mbd.getDependsOn();
            if (dependsOn != null) {
                for (String dep : dependsOn) {
                    registerDependentBean(dep, beanName);
                    getBean(dep);
                }
            }

            // socpe="singleton"的情况
            if (mbd.isSingleton()) {
                // 此处的关键在于创建的匿名对象ObjectFactory及getObject方法,getObject为实际创建bean实例的地方
                sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
                    @Override
                    public Object getObject() throws BeansException {
                        try {
                            // 创建bean的关键
                            return createBean(beanName, mbd, args);
                        }
                        catch (BeansException ex) {
                            destroySingleton(beanName);
                            throw ex;
                        }
                    }
                });②
                bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
            }
            // scope="prototype"的情况
            else if (mbd.isPrototype()) {
                Object prototypeInstance = null;
                try {
                    //创建prototype对象时的前置处理:用于设置ThreadLocal变量,表示bean正在创建
                    beforePrototypeCreation(beanName);
                    //prototype的核心依然是调用createBean
                    prototypeInstance = createBean(beanName, mbd, args);
                }
                finally {
                    //创建prototype对象时的后置处理:移除ThreadLocal变量
                    afterPrototypeCreation(beanName);
                }
                bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
            }
            //scope作用域为其他值的情况:比如request、session及globalSession
            else {
                String scopeName = mbd.getScope();
                final Scope scope = this.scopes.get(scopeName);
                if (scope == null) {
                    throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
                }
                try {
                    //调用实际作用域处理方法,此处也设置了ObjectFactory对象及调用的createBean方法
                    Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
                        @Override
                        public Object getObject() throws BeansException {
                            beforePrototypeCreation(beanName);
                            try {
                                return createBean(beanName, mbd, args);
                            }
                            finally {
                                afterPrototypeCreation(beanName);
                            }
                        }
                    });
                    bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
                }
                catch (IllegalStateException ex) {
                    throw new BeanCreationException(beanName,
                            "Scope '" + scopeName + "' is not active for the current thread; consider " +
                            "defining a scoped proxy for this bean if you intend to refer to it from a singleton",
                            ex);
                }
            }
        }
        catch (BeansException ex) {
            cleanupAfterBeanCreationFailure(beanName);
            throw ex;
        }
    }

    return (T) bean;
}

代码中已经标识比较重要的部分,现在对其进行分析,其中①处:
Object sharedInstance = getSingleton(beanName),具体调用的方法为:

//用于解决单例循环依赖的问题,此处allowEarlyReference为true,即允许早期引用(可能对象刚使用构造函数实例化,还未设置实例的属性等,属于未完全实例化的对象,但是持有该对象的引用,等对象完全初始化后可直接使用)
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        //先从singletonObjects中获取,新创建的此处为空,新建对象在此处返回null后并不会执行
        //下面的代码,会返回,调用②出的getSingleton(beanName, ObjectFactory)方法;
        //只有在存在被依赖并且该实例正在创建的的情况下才会继续执行
        Object singletonObject = this.singletonObjects.get(beanName);
        //如果尚未创建并且此对象为正在创建的单例
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            synchronized (this.singletonObjects) {
               //从earlySingletonObjects中获取,ObjectFactory调用后会将对象放到此处缓存
                singletonObject = this.earlySingletonObjects.get(beanName);
                //此处允许早期引用
                if (singletonObject == null && allowEarlyReference) {
                    //获取ObjectFactory工厂,使用对应的ObjectFactory创建bean
                    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
                        //此处调用注册的匿名ObjectFactory方法,实际调用的是getEarlyBeanReference方法(AbstractAutowireCapableBeanFactory的doCreateBean中注册的,暴露出的实例引用)
                        singletonObject = singletonFactory.getObject();
                        //创建后将此单例设置到earlySingletonObjects缓存
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }
        return (singletonObject != NULL_OBJECT ? singletonObject : null);
    }

标识②中的代码,即:
getSingleton(String beanName, ObjectFactory<?> singletonFactory)方法,此处对对bean进行实例化操作,并将其加入到缓存,代码如下:

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
    //加锁,避免重复执行创建
    synchronized (this.singletonObjects) {
        //双重检查singletonObjects缓存,避免被其他依赖的对象创建完成
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null) {
             ...
            //前置处理:设置到singletonsCurrentlyInCreation中,表示此单例正在创建
            beforeSingletonCreation(beanName);
            boolean newSingleton = false;
            ...

            try {
                //调用getObject方法,实际就是调用createBean方法
                singletonObject = singletonFactory.getObject();③
                newSingleton = true;
            }
            catch (IllegalStateException ex) {
                throw ex
            }
            catch (BeanCreationException ex) {
                throw ex;
            }
            finally {
                //后置处理:从singletonsCurrentlyInCreation中移除,表示对象创建完毕
                afterSingletonCreation(beanName);
            }
            if (newSingleton) {
                //bean完成实例化将其放入singletonObjects缓存,并清除singletonFactories和earlySingletonObjects
                addSingleton(beanName, singletonObject);
            }
        }
        return (singletonObject != NULL_OBJECT ? singletonObject : null);
    }
}

在创建实例后将其放入缓存中:

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);
        }
}

下面分析上面标识③的代码,实际执行的就是注册ObjectFactory的getObject方法,即createBean方法:

@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException {
    
    RootBeanDefinition mbdToUse = mbd;
    ....

    try {
        // 此处针对存在targetClass的bean,在实例化之前执行注册InstantiationAwareBeanPostProcessor
        //的postProcessBeforeInstantiation方法,比如执行AbstractAutoProxyCreateor创建代理类,此处以后介绍
        Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
        if (bean != null) {
            return bean;
        }
    }
    catch (Throwable ex) {
        throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
                "BeanPostProcessor before instantiation of bean failed", ex);
    }
    //创建bean的实例
    Object beanInstance = doCreateBean(beanName, mbdToUse, args);
    if (logger.isDebugEnabled()) {
        logger.debug("Finished creating instance of bean '" + beanName + "'");
    }
    return beanInstance;
}

最终创建bean实例的地方是doCreateBean,此方法比较复杂,单最关键的就几处,分别为:

  • [x] createBeanInstance(beanName, mbd, args)
  • [ ] applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName)
  • [ ] addSingletonFactory(beanName, ObjectFactory)
  • [x] populateBean(beanName, mbd, instanceWrapper)
  • [x] initializeBean(beanName, exposedObject, mbd)
  • [ ] getSingleton(beanName, false)
  • [x] registerDisposableBeanIfNecessary
    其中最重要的为上面重点标识的部分
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
            throws BeanCreationException {

    // Instantiate the bean.
    BeanWrapper instanceWrapper = null;
    ...
    if (instanceWrapper == null) {
        //创建bean的实例:实际调用构造方法进行反射创建实例
        instanceWrapper = createBeanInstance(beanName, mbd, args);①
    }
    ...
    // Allow post-processors to modify the merged bean definition.
    synchronized (mbd.postProcessingLock) {
        if (!mbd.postProcessed) {
            try {
                //bean合并后的处理,Autowired注解正是通过此方法实现诸如类型的预解析
                applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);②
            }
            catch (Throwable ex) {
                throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                        "Post-processing of merged bean definition failed", ex);
            }
            mbd.postProcessed = true;
        }
    }

    // 判断是否需要早期暴露:即是单例、允许循环依赖并且已经正在创建中,
    // 此处设置bean的ObjectFactory,用于bean的暴露,处理循环依赖的情况
    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");
        }
        //实例化后就暴露到singletonFactories缓存中
        addSingletonFactory(beanName, new ObjectFactory<Object>() {
            @Override
            public Object getObject() throws BeansException {
                //如果不存在代理,则直接返回,否则调用SmartInstantiationAwareBeanPostProcessor
                //的getEarlyBeanReference方法,即做增强处理
                return getEarlyBeanReference(beanName, mbd, bean);
            }
        });③
    }

    // Initialize the bean instance.
    Object exposedObject = bean;
    try {
        //处理属性依赖
        populateBean(beanName, mbd, instanceWrapper);④
        if (exposedObject != null) {
            //执行aware、BeanPostProcessor及InitializeBean、init-method处理
            exposedObject = initializeBean(beanName, exposedObject, mbd);⑤
        }
    }
    catch (Throwable ex) {
        if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
            throw (BeanCreationException) ex;
        }
        else {
            throw new BeanCreationException(
                    mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
        }
    }

    if (earlySingletonExposure) {
        //调用getSingleton方法,此处最多执行到earlySingletonObjects缓存中取对象
        Object earlySingletonReference = getSingleton(beanName, false);
        if (earlySingletonReference != null) {
            //如果对bean进行了增强处理,此处为false
            if (exposedObject == bean) {
                exposedObject = earlySingletonReference;
            }
            //处理存在依赖此bean的其他bean的情况
            else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
                //获得依赖此bean的bean名称
                String[] dependentBeans = getDependentBeans(beanName);
                Set<String> actualDependentBeans = new LinkedHashSet<String>(dependentBeans.length);
                for (String dependentBean : dependentBeans) {
                    //如果dependentBean已经实例化,则放入actualDependentBeans中
                    if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
                        actualDependentBeans.add(dependentBean);
                    }
                }
                //在此bean实例化完成之前,依赖此bean的其他bean存在已经实例化完成的情况,
                //不如何先处理依赖bean,再处理被依赖bean的情况
                if (!actualDependentBeans.isEmpty()) {
                    throw new BeanCurrentlyInCreationException(beanName,
                            "Bean with name '" + beanName + "' has been injected into other beans [" +
                            StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
                            "] in its raw version as part of a circular reference, but has eventually been " +
                            "wrapped. This means that said other beans do not use the final version of the " +
                            "bean. This is often the result of over-eager type matching - consider using " +
                            "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
                }
            }
        }
    }

    // 注册DisposableBean和destory-method,用于bean销毁时的回调处理
    try {
        registerDisposableBeanIfNecessary(beanName, bean, mbd);
    }
    catch (BeanDefinitionValidationException ex) {
        throw new BeanCreationException(
                mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
    }

    return exposedObject;
}

综上所述,对于循环依赖,比如A依赖B,B依赖A,在A进行实例化的时候,即调用getBean的时候,执行完createBeanInstance方法,会将A的实例通过ObjectFactory注册到singletonFactories中提前暴露,在设置A的属性的时候,即执行populateBean的时候,发现依赖的属性B尚未实例化,则针对B调用getBean方法,再处理B的实例化时,针对依赖的A会调用getBean的getSingleton方法,进而调用singletonFactories缓存中注册ObjectFactory的getObject方法,即getEarlyBeanReference方法,得到A的实例引用,然后返回处理完成B的实例化,再进行A的实例化处理,进而处理了循环依赖的问题。

3、setter的prototype依赖

Spring无法处理prototype的循环依赖问题,因为无法对prototype作用域的对象进行缓存,会报如下异常:

Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: 

Spring针对循环依赖的分析暂时到这里,其中包含了部分对bean实例化过程的分析,接下来实例化bean的时候具体的处理过程,即上述标识的三个部分,会在下一章节进行分析。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,548评论 6 515
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,497评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,990评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,618评论 1 296
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,618评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,246评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,819评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,725评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,268评论 1 320
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,356评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,488评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,181评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,862评论 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,331评论 0 24
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,445评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,897评论 3 376
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,500评论 2 359