bean创建循环依赖

IOC的bean创建是支持循环依赖的(需配置allow-circular-references=true),看一下源码是如何处理的:

关键类是DefaultSingletonBeanRegistry,里边有3个map:
singletonObjects一级缓存,放已经创建好的bean
earlySingletonObjects二级缓存,放已经构造但是还未填充属性的bean
singletonFactories 三级缓存,放的是它的Factory,即创建createBean的lamda

比如我们有两个bean,DeviceManager和VeeService互相依赖,看一下他们的创建过程
先走到DeviceManager的getBean,
AbstractBeanFactory的

// Create bean instance.
                if (mbd.isSingleton()) {
                    sharedInstance = getSingleton(beanName, () -> {
                        try {
                            return createBean(beanName, mbd, args);
                        }

调用getSingleton,传入一个lamda,实际就是createBean

getSingleton的代码:

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) {
                
                beforeSingletonCreation(beanName);
                boolean newSingleton = false;
                try {
                    singletonObject = singletonFactory.getObject(); //调用传入的lamda,其实就是createBean
                    newSingleton = true;
                }
                
                if (newSingleton) {  // 将创建好的放到一级缓存
                    addSingleton(beanName, singletonObject);
                }
            }
            return singletonObject;
        }
    }

createBean关键部分:

if (instanceWrapper == null) {
            instanceWrapper = createBeanInstance(beanName, mbd, args);//调用构造函数
        }

//如果是单例并且支持循环依赖,并且bean在创建中
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
                isSingletonCurrentlyInCreation(beanName));
        if (earlySingletonExposure) {
            if (logger.isTraceEnabled()) {
                logger.trace("Eagerly caching bean '" + beanName +
                        "' to allow for resolving potential circular references");
            }
//添加到三级缓存,这里添加的是一个lamda
            addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
        }

        // Initialize the bean instance.
        Object exposedObject = bean;
        try {
////填充属性,会递归的去创建所依赖的bean。比如会创建veeService,
由于vee又依赖deviceManager,所以会调用getSingleton的时候,从三级缓存里找到deviceManager并执行lamda创建了提前暴露对象,放到二级缓存
            populateBean(beanName, mbd, instanceWrapper); 

//初始化bean,会创建AOP代理
            exposedObject = initializeBean(beanName, exposedObject, mbd);
        }
        
        if (earlySingletonExposure) {

//这里从二级缓存里拿出来了
            Object earlySingletonReference = getSingleton(beanName, false);
            if (earlySingletonReference != null) {
                if (exposedObject == bean) {
                    exposedObject = earlySingletonReference;
                }
        }

        // Register bean as disposable.
        try {
            registerDisposableBeanIfNecessary(beanName, bean, mbd);
        }

        return exposedObject;

这里createBean就完成了,出来就会把对象放到一级缓存,表示这个bean创建好了
然后getSingleton返回给其他bean去用了

上面就是从deviceManager的视角走完了bean的创建
下来再从veeService的视角看一下有啥不同:

一样的getSingleton调createBean
调构造函数后,放入三级缓存,然后来到populateBean填充属性,遇到deviceManager

调用createBean来创建VeeService,过程和上面的一样,同样会往三级缓存放lamda

然后走到填充属性,需要填充DeviceManager
调用doGetBean,调用DefaultSingletonBeanRegistry的getSingleton
此时DeviceManager还在三级缓存里,所以会从三级缓存拿到,调用lamda创建,放入二级缓存。这个过程还会给DeviceManager创建AOP代理(因为它用了springcache)

然后initializeBean,过程中会检查是否要AOP,结果是不需要
createBean结束后,回到getSingleton,是新建的bean,所以添加到一级缓存
可以看到,vee没有用到二级缓存

如果有三个bean循环依赖,A--》B---》C----》A
创建A,createBeanInstance调用A的构造函数,addSingletonFactory放到三级缓存
填充属性populateBean,getBean找B:
构造B,放三级缓存,填充属性,getBean找C:
构造C,放三级缓存,填充属性:
getBean获取A,getSingleton:
从三级缓存获取lamda执行,A放到二级缓存。过程中会wrapIfNecessary创建AOP
继续创建C,initializeBean,wrapIfNecessary 创建AOP
创建C完成,添加到一级缓存
继续B:initializeBean,wrapIfNecessary 添加到一级缓存
继续A:initializeBean,添加到一级缓存

可以看到,三个bean都有放到三级和一级缓存,但只有A放到了二级缓存

问题:如果关闭循环依赖,二级三级缓存还有没有用?
我认为是没用的。从源码看,添加三级缓存只有addSingletonFactory,它的调用点只有一个,条件是:
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));

必须打开循环依赖开关,否则不会加三级缓存
二级缓存添加在getSingleton,它是从三级缓存获取lamda执行后获得对象放二级缓存,可见二级缓存是依赖三级缓存的

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容