spring 循环依赖问题研究

Posted 子瞻

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring 循环依赖问题研究相关的知识,希望对你有一定的参考价值。

CASE:“A依赖B,同时B又依赖A”。

先贴出来解决循环依赖在DefaultSingletonBeanRegistry中的三个缓存:

    //一级缓存
    Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
    //三级缓存
    Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
    //二级缓存
    Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);

分析:
1.A进行create流程,发现自己依赖B,此时B更没有走create流程。
doCreateBean()中有这样的判断是否允许循环依赖,当前bean是否创建中:

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

isSingletonCurrentlyInCreation(beanName)的具体实现是:

protected void beforeSingletonCreation(String beanName) {
        if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }
    }

那么它的调用时机又是什么时候呢?
DefaultSingletonBeanRegistry.getSingleton();
AbstractBeanFactory.doGetBean()中也就是AbstractBeanFactory.getBean()调用了上面这个方法.
在这里还有一个调用链路就是:
AbstractApplicationContext.finishBeanFactoryInitialization(beanFactory)->

    beanFactory.preInstantiateSingletons()->AbstractBeanFactory.getBean().

在这里,我目前先讨论这两个主要的调用链路,其他的以后再说。
我们接着分析,如果判断出循环引用,则执行addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean))。
看一下具体实现:

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,是不是很兴奋呢;此时,A将自己放入到singletonFactories中。
2.发现自己依赖对象B,此时就尝试去get(B),发现B还没有被create,所以B走create流程。
3.B初始化时时候发现自己依赖A,尝试get(A),从singletonObjects(一级缓存)中取,发现没有(此时A还没有初始化完成),earlySingletonObjects(二级缓存)中也没有,最后从singletonFactories(三级缓存)中取,因为在1中A已经存放在singletonFactories里面了,所以B可以拿到A(虽然是个半成品)。
4.此时,B可以顺利完成初始化,并将自己放在singletonObjects中。

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
if (this.singletonsCurrentlyInDestruction) {
                boolean newSingleton = false;
                boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
                if (recordSuppressedExceptions) {
                    this.suppressedExceptions = new LinkedHashSet<>();
                }
                try {
                    singletonObject = singletonFactory.getObject();
                    newSingleton = true;
                }
                if (newSingleton) {
                    //添加到singletonObjects中
                    addSingleton(beanName, singletonObject);
                }
}

下面看,具体实现:

protected void addSingleton(String beanName, Object singletonObject) {
        synchronized (this.singletonObjects) {
            this.singletonObjects.put(beanName, singletonObject);
            this.singletonFactories.remove(beanName);
            this.earlySingletonObjects.remove(beanName);
            this.registeredSingletons.add(beanName);
        }
    }

5.此时返回A中,A此时能拿到B,最终A也完成了初始化,进去了一级缓存singletonObjects中。

以上是关于spring 循环依赖问题研究的主要内容,如果未能解决你的问题,请参考以下文章

spring练习,在Eclipse搭建的Spring开发环境中,使用set注入方式,实现对象的依赖关系,通过ClassPathXmlApplicationContext实体类获取Bean对象(代码片段

Spring源代码解析 ---- 循环依赖

Java面试小短文Spring 如何解决循环依赖?

Java面试小短文Spring 如何解决循环依赖?

Java面试小短文Spring 如何解决循环依赖?

Java面试小短文Spring 如何解决循环依赖?