Spring是如何解决循环依赖的?

什么叫循环依赖:简单来说,在spring中对象的创建管理都是IOC容器帮我们做的,以默认单例的方式帮我们创建bean,但是会出现一个问题:对象A里面有个属性是B对象,B对象里面又有一个A对象。如下

class A {
    private B b;
}
class B{
     private A a;
}

不管先创建哪个对象,在属性赋值时都要需要另一个对象。假设创建A对象,需要属性赋值的时候,发现需要B对象,去创建B对象的时候,发现需要A对象,此时A对象还未创建完,这样就形成了一个简单的循环依赖问题。

流程图:

          此时容器中当然没有A和B对象,可以发现此时已经形成了一个闭环,也就是循环依赖。解决的关键在于:下图的最后一步没找到

 此时若能解决此处的问题,闭环将被打破。关键点在于此时A对象已经实例化了,但还未完成初始化(半成品对象)。

         这时候,请思考一个问题,如果我们持有一个对象的引用(C中的指针),能否在后续的步骤中给这个对象进行赋值操作?

当然是可以的,在这个问题的基础上,我们可以引入一个东西,来解决循环依赖,就是大家所熟悉的缓存。稍稍加点改动,只需要我们实例化后将对象放入缓存,从容器中查找时,也查找缓存就可以解决循环依赖。此时B已经是成品对象了并且在缓存中,然后A对象就可以完成初始化了。

 当然成品对象和半成品对象放在一起怎么看都不合适,所以spring中解决循环依赖是:三级缓存和提前暴露。其实最核心的是实例化和初始化分开进行。

spring三级缓存源码:

    //一级缓存,保存beanName和创建Bean直接实例的关系(保存成熟对象)
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
    //三级缓存,保存beanName和创建Bean的工厂之间的关系(第二个是一个函数式接口的类型)
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
    //二级缓存,保存保存beanName和创建Bean直接实例的关系(半成品对象)
    private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap(16);

简述下过程:

A首先完成了初始化的第一步,而且将本身提早曝光到singletonFactories中,此时进行初始化的第二步,发现本身依赖对象B,此时就尝试去get(B),发现B尚未被create,因此走create流程,B在初始化第一步的时候发现本身依赖了对象A,因而尝试get(A),尝试一级缓存singletonObjects(确定没有,由于A还没初始化彻底),尝试二级缓存earlySingletonObjects(也没有),尝试三级缓存singletonFactories,因为A经过ObjectFactory将本身提早曝光了,因此B可以经过ObjectFactory.getObject拿到A对象(半成品),B拿到A对象后顺利完成了初始化阶段一、二、三,彻底初始化以后将本身放入到一级缓存singletonObjects中。此时返回A中,A此时能拿到B的对象顺利完成本身的初始化阶段二、三,最终A也完成了初始化,进去了一级缓存singletonObjects中,因为B拿到了A的对象引用,因此B里面A对象完成了初始化。

 注意事项:

1.Spring只是解决了单例模式下属性依赖的循环问题;Spring为了解决单例的循环依赖问题,使用了三级缓存。

2.多实例Bean是每次调用一次getBean都会执行一次构造方法并且给属性赋值,根本没有三级缓存,因此不能解决循环依赖。

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值