菜鸡的Spring分析——循环依赖问题

1 什么是循环依赖

如下代码所示,类A里面依赖了类B,而B里面有依赖了A

Spring循环依赖指的是创建对象过程中,需要实例化它的依赖对象,但是它的依赖对象最终还会依赖到A,最终形成一个闭环。

@Component
public class A {
  private B b;
  public void setB(B b) {
    this.b = b;
  }
}
@Component
public class B {
  private A a;
  public void setA(A a) {
    this.a = a;
  }
}

循环依赖图示:

2 Spring循环依赖有几种?

1.原型模式循环依赖[无法解决]

2.单例Bean循环依赖-构造参数产生依赖[无法解决]

3.单例Bean循环依赖-setter产生依赖[可以解决]

3 具体措施

3.1 三级缓存

1. singletonObjects: 缓存某个beanName对应的经过了完整生命周期的bean

2. earlySingletonObjects:缓存提前拿原始对象进行了AOP之后得到的代理对象,原始对象还没有进行属性注入和后续的BeanPostProcessor等生命周期

3. singletonFactories:缓存的是一个ObjectFactory,主要用来生成原始对象进行了AOP之后得到的代理对象,在每个Bean的生成过程中,都会提前暴露一个厂,这个工厂可能用到,也可能用不到,如果没有出现循环依赖依赖本bean,那么这个工厂无用,本bean按照自己的生命周期执行,执行完后直接把本bean放入
singletonObjects中即可,如果出现了循环依赖依赖了本bean,则另外那个bean执行ObjectFactory提交得到一个AOP之后的代理对象(如果有AOP的话,如果无需AOP,则直接得到一一个原始对象)。

4.其实还要一个缓存, 就是earlyProxyReferences, 它用来记录某个原始对象是否进行过AOP了。

3.2 简单理解循环依赖的解决

在A的原始对象创建时放到缓存里面去,在B需要用A的时候在缓存池里面获取

 

 

3.3 单例Bean循环依赖-构造参数产生依赖的判断(无法解决,只能抛出异常)

步骤1:获取合并后的BeanDefinition信息

步骤2:缓存中查...因为当前对象未初始化过,所以-定拿不到! 

步骤3:获取mbd,确定mbd可以创建实例,即mbd为非抽象的。

步骤4:优先加载当前mbd依赖的DenpendsOn BeanDefinition。

步骤5: 一级缓存尝试获取,一定拿不到。

步骤6:记录当前BeanName到CreationSet中!

创建实例流程开始

步骤7:通过Class拿到Constructor构造器,进行反射创建对象

步骤8:发现构造器有一个参数是类型

步骤9:需要先实例化B

实例化B,单例
步骤1~4.....

步骤5:一级缓存尝试获取,一定拿不到

步骤6:记录当前BeanName到CreationSet中

步骤7:通过Class拿到Constructor构造器,进行反射创建对象

步骤8:发现构造器有一个参数是类型A

步骤9:需要先实例化A

实例化A

步骤1-4

步骤5: 一级缓存尝试获取,一定拿不到

步骤6:记录当前BeanName到CreationSet中,但是失败了,因为set中已经有A了,检测出循环依赖

========>抛出循环依赖异常

3.4 原型模式循环依赖的判断,无法解决,只能抛出异常

步骤1~4和上述单例模式构造器一样

原型A创建对象

步景5:向rertynCreationSet中添如当前beanName

步骤6:通过Cass拿到Constructor构造器,将要进行反射创建对象

步骤7:发现构造器有个参数类型

步骤8:需要先实例化B

实例化B,B可能为单例也可能为原型,经历了步骤n
步骤n+1:发现的构造器方法有个参数类型

原型A创建对象

步骤1~4
步骤5:向Set中添加当前beanName,但是失败了因为set中已经有了,检测出循环依

步骤7:抛出循环依赖的异常! 

3.5 AOP的情况下

在有AOP的情况下,在单例池里面生成的是代理对象,而B里面获取的那个却是A的对象(未经过AOP的对象)这就会产生问题

构造AService对应的bean的过程:

1.扫描类--->BeanDefinition

2.aService = new AService( );生成原始对象,将lambda表达式放到三级缓存里面区(是一个Object工厂,这是不会去执行,仅仅是放进去)

3. aService填充属性b -- >BService的Bean,会进行BService的Bean的生成

4. 执行Aware,init等方法

5.BeanPostProcessor :对前面所生成的对象进行加工,第五步的时候会进行判断,如果已经在BService里面生成了A的代理对象,则不会在进行AOP的操作,而是去二级缓存里面去拿 到AOP的代理对象

6.从二级缓存里面去拿AOP之后的代理对象

7.单例池(s ingletonObjects, beanName: bean对象)

 

posted @ 2020-11-05 11:36  Mistolte  阅读(218)  评论(0)    收藏  举报