> 文章列表 > Spring之循环依赖

Spring之循环依赖

Spring之循环依赖

什么事循环依赖

很简单的定义就是就如有两个对象A类,B类,其中两个类中的属性都有对方。

A类

public class A{private B b;}

B类

public class B{
private A a;
}

在Spring中,什么情况下会出现循环依赖

如果要了解循环依赖,首先需要知道Spring中Bean的生命周期。

Bean的生命周期:

  1. Spring扫描class文件获取得到BeanDefinition;
  2. 根据得到的BeanDefinition去生成Bean。
  3. 根据推断构造方法得到实例化的构造方法。
  4. 根据得到的构造方法去实例化一个对象。
  5. 根据得到的对象填充属性(也叫做依赖注入)。
  6. 如果对象中的某个方法被AOP了,则需要为该对象生成一个代理对象。
  7. 最终把代理对象放入单例池中。

根据文章开头提到的的循环依赖,当A对象实例化出来得到一个对象,需要开始对B属性进行赋值,如果发现单例池中没有B对象,则需要开始创建B对象。而在创建完B对象填充A属性的时候,发现A对象也不在单例池中,这个时候就出现了循环依赖。
根据这种情况,Spring为了解决这种问题,提出来了一种机制,这种机制就是三级缓存

三级缓存:

三级缓存分别是指:
1级缓存:singletonObjects: 缓存的是已经经历过完成Bean的生命周期的Bean对象。
2级缓存: earltSingletonObjects;相比singletonObjects中的Bean,表示缓存的是早期的Bean对象,并没有走完完整的Bean的生命周期。
3级缓存: singletonFactories; 缓存的是ObjectFactory,表示对象工厂,用来创建早期Bean对象的工厂。

如何解决循环依赖

为什么会发生循环依赖
Spring之循环依赖
在A对象创建时,需要B属性,而B属性在单例池中不存在,需要去创建B对象,在填充B对象的属性时,发现需要A对象,而A对象同样不在单例池中。此时就发生了以上的情况。

Spring如何解决这种情况呢?
Spring之循环依赖
A对象在创建B的过程中,在进行依赖注入之前,先把刚实例化出来的对象放入缓存中,再进行依赖注入。此时A依赖了B对象,发现B对象不存在,则需要把B对象创建出来,而B对象创建的过程和A对象一样,也是先实例化出来一个对象放入缓存中,然后开始B对象的依赖注入,此时B对象需要A属性就可以从缓存中获取到,B对象就可以完整走完Bean的生命周期,同时A对象也可以完整走完。

通过以上流程分析,只需要加入一个缓存就可以解决Spring的循环依赖问题,为什么还需要第三个缓存个呢?

加入A对象中有某个方法被AOP了呢?此时在A对象初始化后需要生成一个代理对象。而我们通过以上分析给B赋值的是A对象的原始对象,并非一个代理对象,这就出现了问题,为了解决这种问题,Spring引入了第三个缓存,ObjectFactories。

正常情况下:
1、实例化A对象;
2、A对象属性填充;
3、基于AOP生成一个代理对象;
4、把代理对象放入单例池singletonObjects中。

为了保证放入单例池的代理对象和B对象的A属性是同一个对象,就是需要利用第三级缓存singletonFactories.
1、在实例化出来A对象后,会构造出来一个ObjectFactory对象放入singletonFatories。
Spring之循环依赖
而这个ObjectFactory是一个函数式接口。
Spring之循环依赖
该方法会执行SmartInstantiationAwareBeanPostProcessor中的getEarlyBeanRefrece方法,这个方法首先得到一个缓存key,缓存key就是beanname, 然后把beanName和Bean存储earlyRefreces中,调用wraplfNecessary进行AOP,得到一个代理对象。

有了singletonFactories之后,当实例化完成A对象后,会把A对象包装成一个ObjectFactory,存入singletonFactories中,开始填充B属性,实例化B对象之后,填充A属性,首先从单例池中尝试获取,没有的话尝试从二级缓存中获取,如果还是没有,则真正开始执行三级缓存中缓存的ObjectFactory,将得到的对象存入二级缓存中,同时删除三级缓存中的对象。此时A对象和B对象都可以完成创建,同时是否需要进行AOP交由Spring来决定。
Spring之循环依赖