> 文章列表 > Spring IoC循环依赖问题

Spring IoC循环依赖问题

Spring IoC循环依赖问题

什么是循环依赖

循环依赖其实就是循环引⽤,也就是两个或者两个以上的 Bean 互相持有对⽅,最终形成闭环。⽐如A
依赖于B,B依赖于C,C⼜依赖于A。
Spring IoC循环依赖问题
注意,这⾥不是函数的循环调⽤,是对象的相互依赖关系。循环调⽤其实就是⼀个死循环,除⾮有终结
条件。
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容器中

循环依赖源码分析

Spring IoC循环依赖问题
Spring IoC循环依赖问题

(1)A实例化过程之后立马放入三级缓存(提前暴露自己)

refresh -> finishBeanFactoryInitialization-> getBean -> doGetBean -> createBean -> doCreateBean中

Spring IoC循环依赖问题

  • 判断A存在循环依赖,将A对象放入到三级缓存singletonFactories
	// 解决循环依赖的关键步骤,判断是否存在循环依赖:单例 && 允许循环依赖 && 当前bean正在创建中boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&isSingletonCurrentlyInCreation(beanName));// 如果需要提前暴露单例Bean,则将该Bean放入三级缓存中if (earlySingletonExposure) {if (logger.isDebugEnabled()) {logger.debug("Eagerly caching bean '" + beanName +"' to allow for resolving potential circular references");}// 将刚创建的bean放入三级缓存中singleFactories(key是beanName,value是FactoryBean)//如果支持则暴露一个工厂addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));}
	protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {Assert.notNull(singletonFactory, "Singleton factory must not be null");synchronized (this.singletonObjects) {/*** 如果单例池当中不存在才会add* 因为这里主要为了循环依赖服务的代码* 如果bean存在单例池的话其实已经是一个完整的bean了* 一个完整的bean自然也是已经完成属性注入,循环依赖已经依赖上了* 所以如果这个对象已经是一个完整的bean,就不需要关心,不需要进入 if* 如果一级缓存中没有该实例,则放入三级缓存中*/if (!this.singletonObjects.containsKey(beanName)) {// 放入三级缓存this.singletonFactories.put(beanName, singletonFactory);/*** 从三级缓存中remove掉当前的bean* 为什么要remove?抛开细节,这三个map当中其实其实存的都是一个对象* spring的做法是三个不能同时存在*/// 从二级缓存中移除this.earlySingletonObjects.remove(beanName);//将beanName注册到registeredSingletons缓存(已经注册的单例集合)this.registeredSingletons.add(beanName);}}}
  • A对象中需要进行属性注入,发现依赖了B对象

Spring IoC循环依赖问题
Spring IoC循环依赖问题

(2)B对象实例化过程和A实例化过程一样,将自己放入到三级缓存中之后下一步进行属性注入发现依赖于A,那么就去三级缓存使用尚未成型的Bean A,并将A升级放到二级缓存中。

  • B对象实例化放入三级缓存中

Spring IoC循环依赖问题

  • B对象中需要进行属性注入,发现依赖了A对象

Spring IoC循环依赖问题
Spring IoC循环依赖问题

(3) 再次加工Bean A的过程以及放入二级缓存示意图

Spring IoC循环依赖问题

(4)B对象创建完成之后放入一级缓存中

Spring IoC循环依赖问题
(5)A对象从一级缓存中获取到B对象,注入到A对象中
Spring IoC循环依赖问题