Spring IoC循环依赖问题

一. 什么是循环依赖

循环依赖其实就是循环引⽤,也就是两个或者两个以上的 Bean 互相持有对⽅,最终形成闭环。⽐如A依赖于B,B依赖于C,C⼜依赖于A

注意,这⾥不是函数的循环调⽤,是对象的相互依赖关系。循环调⽤其实就是⼀个死循环,除⾮有终结条件。

Spring中循环依赖场景有:

构造器的循环依赖(构造器注⼊)
Field 属性的循环依赖(set注⼊)

其中,构造器的循环依赖问题⽆法解决,只能拋出 BeanCurrentlyInCreationException 异常,在解决属性循环依赖时,spring采⽤的是提前暴露对象的⽅法。

二. 循环依赖处理机制

 单例 bean 构造器参数循环依赖(⽆法解决)

prototype 原型 bean循环依赖(⽆法解决)
对于原型bean的初始化过程中不论是通过构造器参数循环依赖还是通过setXxx⽅法产⽣循环依
赖,Spring都 会直接报错处理。
AbstractBeanFactory.doGetBean()⽅法:

if (isPrototypeCurrentlyInCreation(beanName)) {
 throw new BeanCurrentlyInCreationException(beanName);
}
protected boolean isPrototypeCurrentlyInCreation(String beanName) {
 Object curVal = this.prototypesCurrentlyInCreation.get();
 return (curVal != null &&
 (curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>)
curVal).contains(beanName))));
}

在获取bean之前如果这个原型bean正在被创建则直接抛出异常。原型bean在创建之前会进⾏标记
这个beanName正在被创建,等创建结束之后会删除标记

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

总结:Spring 不⽀持原型 bean 的循环依赖。

单例bean通过setXxx或者@Autowired进⾏循环依赖

Spring 的循环依赖的理论依据基于 Java 的引⽤传递,当获得对象的引⽤时,对象的属性是可以延
后设置的,但是构造器必须是在获取引⽤之前

Spring通过setXxx或者@Autowired⽅法解决循环依赖其实是通过提前暴露⼀个ObjectFactory对
象来完成的,简单来说ClassA在调⽤构造器完成对象初始化之后,在调⽤ClassA的setClassB⽅法
之前就把ClassA实例化的对象通过ObjectFactory提前暴露到Spring容器中。

1. 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);
 }
 });
 }

2. ClassA调⽤setClassB⽅法,Spring⾸先尝试从容器中获取ClassB,此时ClassB不存在Spring
容器中。

3. Spring容器初始化ClassB,同时也会将ClassB提前暴露到Spring容器中

4. ClassB调⽤setClassA⽅法,Spring从容器中获取ClassA ,因为第⼀步中已经提前暴露了
ClassA,因此可以获取到ClassA实例

         ClassA通过spring容器获取到ClassB,完成了对象初始化操作。

5. 这样ClassA和ClassB都完成了对象初始化操作,解决了循环依赖问题。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值