Bean循环依赖问题

什么是循环依赖?

你拥有两个类、一个是classA、一个是classB、两个类之间相互引用就会造成循环依赖

public class ClassA{

    @Autowired
    ClassB classB;

}
public class ClassB{

    @Autowired
    ClassA classA;

}

根据Bean生命周期来说、第一步实例化Bean、第二步赋值、

1、实例化ClassA

2、给ClassA中的属性ClassB赋值、但是因为ClassB又有属性ClassA中、那么当ClassB实例化完、执行第二步给ClassB中的ClassA对象赋值之后、是否继续再实例化一个ClassA?如果按照这个理解、那不是无穷尽、无限初始化?事实真的如此?

接下来、看看Spring如何解决这个循环依赖问题:

在默认情况下、由上述ClassA跟ClassB的项目启动并不会报错、而是正常启动、这就是大名鼎鼎的三级缓存作用

先说循环依赖产生的场景问题(不说场景都是耍流氓):

场景一(Spring默认场景):

Bean作用域:单例(singleton)

注入方式: set注入

代码如最上面的ClassA跟ClassB、这种方式存在循环依赖的情况下Spring并不会报错、而是正常启动

场景二:

Bean作用域:单例(singleton)

注入方式: 构造注入

public class ClassA{
    
    ClassB classB;
    public ClassA(ClassB classB){
        this.classB = classB;
    }

}

启动报错

Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'classB': Requested bean is currently in creation: Is there an unresolvable circular reference?
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:265)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
	at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:325)
	... 44 more

场景三:

Bean作用域:原型(prototype)

注入方式: set注入

启动报错、错误如上

那为什么场景一就没问题、而场景二、三就存在问题呢、下面具体分析一下Spring循环依赖解决原理

在场景一的情况下、之所以能够解决循环依赖的问题、是因为这种方式可以将实例化Bean和Bean属性赋值分开两个动作去完成、

实例化Bean的时候:调用无参构造方法完成、此时先不给属性赋值、而是将自己“曝光”给外界

Bean属性赋值的时候:调用set方法来赋值

两个步骤可以完全分开去完成、并且不要求同一时间点完成

实例化Bean的时候、曝光、就是实例化完成以后、先将自己放到缓存中、当所有Bean实例化完成以后、再一个个调用set方法给属性赋值、这样就解决了循环依赖问题、这个解决办法的实现、就是所谓的三级缓存

下面分析一下源码:

 这三个缓存其实本质上是三个Map集合。

【一级缓存】singletonObjects

Cache of singleton objects: bean name to bean instance.

单例对象的缓存:key存储bean名称,value存储Bean对象

【二级缓存】earlySingletonObjects

Cache of early singleton objects: bean name to bean instance.

早期单例对象的缓存:key存储bean名称,value存储早期的Bean对象

【三级缓存】singletonFactories

Cache of singleton factories: bean name to ObjectFactory.

单例工厂缓存:key存储bean名称,value存储该Bean对应的ObjectFactory对象

我们再来看,在该类中有这样一个方法addSingletonFactory(),这个方法的作用是:将创建Bean对象的ObjectFactory对象提前曝光。

 

先从一级缓存中取、找不到再到二级缓存中取、找不到就到三级缓存获取之前曝光的ObjectFactory对象、通过ObjectFactory对象获取Bean实例

 

这就解决了场景一的情况下、循环依赖的问题、先把所有Bean都实例化、在ClassA实例化完给其中的属性ClassB赋值的时候、通过ClassB提前曝光出来的ObjectFactory对象拿到实例化的ClassB、同理、ClassB实例化过程中、因为ClassA已经曝光过了、所以也拿到ClassA的实例、这样就解决了循环依赖的问题

总结:

Spring只能解决setter方法注入的单例bean之间的循环依赖。ClassA依赖ClassB,ClassB又依赖ClassA,形成依赖闭环。Spring在创建ClassA对象后,不需要等给属性赋值,直接将其曝光到bean缓存当中。在解析ClassA的属性时,又发现依赖于ClassB,再次去获取ClassB,当解析ClassB的属性时,又发现需要ClassA的属性,但此时的ClassA已经被提前曝光加入了正在创建的bean的缓存中,则无需创建新的的ClassA的实例,直接从缓存中获取即可。从而解决循环依赖问题

学习动力节点老杜Spring6相关视频

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值