ioc循环依赖问题

什么是循环依赖

循环依赖就是循环引用,两个或者两个以上的Bean互相引用对方,最终形成闭环。
例如:A依赖于B,B依赖于C,C依赖于A
在这里插入图片描述
注意:这里说的不是函数的循环调用,而是对象的互相引用

Spring循环依赖的场景

构造器的循环依赖(构造器注入)
Field属性循环依赖(set注入)
其中构造器的循环依赖是无法解决的,只能报BeanCurrentlyInCreationException异常,prototype类型的bean的set注入也无法解决,而解决set注入循环依赖问题,spring是通过三级缓存提前将bean对象暴露的方式。
我们看AbstractBeanFactory.doGetBean()方法

if (isPrototypeCurrentlyInCreation(beanName)) {
 throw new BeanCurrentlyInCreationException(beanName);
}

spring会判断正在创建的Prototype类型的bean是否已经正在被创建,如果正在创建就会报错

try {
 //创建原型bean之前添加标记
 beforePrototypeCreation(beanName);
 //创建原型bean
 prototypeInstance = createBean(beanName, mbd, args);
}
finally {
 //创建原型bean之后删除标记
 afterPrototypeCreation(beanName);
}

Prototype bean 在创建之前会被标记正在创建,等创建过后会删除标记

循环依赖的处理机制

假设现在有Bean A和Bean B他们之间互相依赖,那么spring是如何利用三级缓存去解决互相依赖问题的呢?我们用一张图来解释
在这里插入图片描述
Spring通过setXxx或者@Autowired方法解决循环依赖其实是通过提前暴露⼀个ObjectFactory对象来完成的,简单来说ClassA在调用构造器完成对象初始化之后,在调⽤ClassA的setClassB方法
之前就把ClassA实例化的对象通过ObjectFactory提前暴露到Spring容器中。
Spring容器初始化ClassA通过构造器初始化对象后提前暴露到Spring容器。

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");
 }
 //将初始化后的对象提前已ObjectFactory对象注⼊到容器中
 addSingletonFactory(beanName, new ObjectFactory<Object>() {
 @Override
 public Object getObject() throws BeansException {
 return getEarlyBeanReference(beanName, mbd, bean);
 }
 }

ClassA调用setClassB方法,Spring首先尝试从容器中获取ClassB,此时ClassB不存在Spring容器中。
Spring容器初始化ClassB,同时也会将ClassB提前暴露到Spring容器中
ClassB调用setClassA方法,Spring从容器中获取ClassA ,因为第⼀步中已经提前暴露了ClassA,因此可以获取到ClassA实例
ClassA通过spring容器获取到ClassB,完成了对象初始化操作。
这样ClassA和ClassB都完成了对象初始化操作,解决了循环依赖问题。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值