有两个Bean对象,A对象中有一个属性的B对象,B对象中有一个属性的A对象,他们都需要依赖注入,但是map单例池中都没有这个对象。正常情况下在进行注入时,如果map单例池中没有需要的Bean对象B就会去创建这个Bean对象B,但如果需要创建的这个Bean对象B又需要依赖注入对象A,这就造成了循环依赖问题。
Spring使用了三级缓存来解决循环依赖。
- 第一级缓存:单例池 singletonObjects,它用来存放经过完整Bean生命周期过程的单例Bean对象
- 第二级缓存:earlySingletonObjects,它用来保存哪些没有经过完整Bean生命周期的单例Bean对象,用来保证不完整的bean也是单例
- 第三级缓存:singletonFactories,它保存的就是一个lambda表达式,它主要的作用就是bean出现循环依赖后,某一个bean到底会不会进行AOP操作
现在有两个Bean,进行循环依赖
@ComponentpublicclassAService{@AutowiredBService bService;publicvoidtest(){System.out.println(bService);}}
@ComponentpublicclassBService{@AutowiredAService aService;}
现在创建Bean的过程如下:
AService 创建的生命周期
- 推断构造函数,创建AService的普通对象
- 依赖注入,为bService属性赋值—>去单例池SingletonObjects中找BService —> 如果没有就创建BService的Bean对象
- 填充其他属性
- 初始化前操作 @PostConstruct
- 初始化 InitialzingBean接口中的afterPropertiesSet()
- 初始化后 AOF
- 放入单例池
BService 创建的生命周期
- 推断构造函数,创建BService的普通对象
- 依赖注入,为aService属性赋值—>去单例池中找AService对象 —> 找不到就创建AService
- 初始化前操作 @PostConstruct
- 初始化 InitialzingBean接口中的afterPropertiesSet()
- 初始化后 AOF
- 放入单例池
这就造成了循环依赖
可以引入一个map来解决循环依赖问题,在创建AService普通对象的时候,将这个普通对象存入map中,然后在BService创建的生命周期中进行依赖注入时,如果在单例池中没有找到AService对象就去map中找。
AService 创建的生命周期
- 推断构造函数,创建AService的普通对象 —> hushangMap<beanName, 普通对象>
- 依赖注入,为bService属性赋值—>去单例池SingletonObjects中找BService —> 如果没有就创建BService的Bean对象
- 如果有其他属性就填充其他属性
- 初始化前操作 @PostConstruct
- 初始化 InitialzingBean接口中的afterPropertiesSet()
- 初始化后 AOF
- 放入单例池
BService 创建的生命周期
- 推断构造函数,创建BService的普通对象
- 依赖注入,为aService属性赋值—>去单例池中找AService对象 —> 去hushangMap中找,能够找到AService的普通对象,就不用去创建了
- 初始化前操作 @PostConstruct
- 初始化 InitialzingBean接口中的afterPropertiesSet()
- 初始化后 AOF
- 放入单例池
我们可以发现,到现在为止就使用了两个map,所以明明Spring使用两级缓存就能解决循环依赖为什么最后还要使用三级缓存嘞?肯定是有一些缺陷的。比如,可能AService会进行AOP操作,会创建AServiceProxy代理对象,然后将代理对象放入单例池中,但是BService进行属性赋值 依赖注入的时候是把AService的普通对象进行赋值,所以这里是有问题的,应该是赋值AServiceProxy代理对象。
解决这个问题的思路就是,将AOP的操作提前,如果AService要进行AOP的话,那就就将代理对象放入hushangMap中,而不是放普通对象
AService 创建的生命周期
- 推断构造函数,创建AService的普通对象 —> AOP —> hushangMap<beanName, **代理对象**>
- 依赖注入,为bService属性赋值—>去单例池SingletonObjects中找BService —> 如果没有就创建BService的Bean对象
- 如果有其他属性就填充其他属性
- 初始化前操作 @PostConstruct
- 初始化 InitialzingBean接口中的afterPropertiesSet()
- 初始化后 AOF
- 放入单例池
BService 创建的生命周期
- 推断构造函数,创建BService的普通对象
- 依赖注入,为aService属性赋值—>去单例池中找AService对象 —> 去hushangMap中找 —> 得到AService代理对象
- 初始化前操作 @PostConstruct
- 初始化 InitialzingBean接口中的afterPropertiesSet()
- 初始化后 AOF
- 放入单例池
现在我们需要在第一步就去判断该对象是否要进行AOP操作,我们不可能所有的Bean在第一步的时候就去进行AOP,因为在Spring中整体的设计是在第5步才会去进行AOP,只有在某种特殊的情况下才需要在第一步去进行AOP。这个特殊情况就是当前Bean出现了循环依赖的情况下才需要提前进行AOP。
AService 创建的生命周期
出现了循环依赖的情况下才需要提前进行AOP。
- 推断构造函数,创建AService的普通对象 —> 是否满足循环依赖条件?—> AOP —> hushangMap<beanName, 代理对象>
- 依赖注入,为bService属性赋值—>去单例池SingletonObjects中找BService —> 如果没有就创建BService的Bean对象
- 如果有其他属性就填充其他属性
- 初始化前操作 @PostConstruct
- 初始化 InitialzingBean接口中的afterPropertiesSet()
- 初始化后 AOF
- 放入单例池
BService 创建的生命周期
- 推断构造函数,创建BService的普通对象
- 依赖注入,为aService属性赋值—>去单例池中找AService对象 —> 去hushangMap中找 —> 得到AService代理对象
- 初始化前操作 @PostConstruct
- 初始化 InitialzingBean接口中的afterPropertiesSet()
- 初始化后 AOF
- 放入单例池
我们能够发现,在AService创建中第一步的时候 不太好判断当前Bean是否产生了循环依赖,在BService创建的第二步中才比较好判断是否产生了循环依赖。我们可以再创建AService的时候,将当前创建的Bean的名字存入一个createSet集合中。在BService创建中依赖出入时,如果在单例池中没有找到需要的bean,那就去createSet集合中判断有没有我们需要的Bean,如果有就表示当前需要的Bean对象它自己正在创建中,这也就是循环依赖
AService 创建的生命周期
- creatingSet(“AService”)
- 推断构造函数,创建AService的普通对象 —> hushangMap<beanName, 普通对象>
- 依赖注入,为bService属性赋值—>去单例池SingletonObjects中找BService —> 如果没有就创建BService的Bean对象
- 如果有其他属性就填充其他属性
- 初始化前操作 @PostConstruct
- 初始化 InitialzingBean接口中的afterPropertiesSet()
- 初始化后 AOF
- 放入单例池
BService 创建的生命周期
- 推断构造函数,创建BService的普通对象
- 依赖注入,为aService属性赋值—>去单例池中找AService对象 —>createSet找 —> 出现了循环依赖 —>提前AOP 生成AServiceProxy代理对象,如果Bean本来就不需要进行AOP操作这里就还是获取的普通对象
- 初始化前操作 @PostConstruct
- 初始化 InitialzingBean接口中的afterPropertiesSet()
- 初始化后 AOF
- 放入单例池
现在还是有问题的,假如现在有三个Bean,进行循环依赖
@ComponentpublicclassAService{@AutowiredBService bService;@AutowiredCService cService;publicvoidtest(){System.out.println(bService);}}
@ComponentpublicclassBService{@AutowiredAService aService;}
@ComponentpublicclassCService{@AutowiredAService aService;}
那么现在的创建过程如下:
AService 创建的生命周期
- creatingSet(“AService”)
- 推断构造函数,创建AService的普通对象 —> hushangMap<beanName, 普通对象>
- 依赖注入,为bService属性赋值—>去单例池SingletonObjects中找BService —> 如果没有就创建BService的Bean对象
- 依赖注入,为cService属性赋值—>去单例池SingletonObjects中找CService —> 如果没有就创建CService的Bean对象
- 如果有其他属性就填充其他属性
- 初始化前操作 @PostConstruct
- 初始化 InitialzingBean接口中的afterPropertiesSet()
- 初始化后 AOF
- 放入单例池
BService 创建的生命周期
- 推断构造函数,创建BService的普通对象
- 依赖注入,为aService属性赋值—>去单例池中找AService对象 —>createSet找 —> 出现了循环依赖 —>提前AOP 生成AServiceProxy代理对象
- 初始化前操作 @PostConstruct
- 初始化 InitialzingBean接口中的afterPropertiesSet()
- 初始化后 AOF
- 放入单例池
CService 创建的生命周期
- 推断构造函数,创建CService的普通对象
- 依赖注入,为aService属性赋值—>去单例池中找AService对象 —>createSet找 —> 出现了循环依赖 —>提前AOP 生成AServiceProxy代理对象
- 初始化前操作 @PostConstruct
- 初始化 InitialzingBean接口中的afterPropertiesSet()
- 初始化后 AOF
- 放入单例池
可以发现上方创建了两个AServiceProxy代理对象,分别赋值给了BService和CService中的属性,这不符合单例Bean,应该的一个AServiceProxy代理对象放入单例池,并且是这个对象进行依赖注入。
解决这个问题就需要使用第二级缓存 earlySingletonObjects,将创建好的AServiceProxy代理对象放入earlySingletonObjects中,key就是beanName,value就是创建好的代理对象,然后CService就从这里拿。
earlySingletonObjects存放的是还没有经过完整生命周期的单例bean对象。
AService 创建的生命周期
- creatingSet(“AService”)
- 推断构造函数,创建AService的普通对象 —> hushangMap<beanName, 普通对象>
- 依赖注入,为bService属性赋值—>去单例池SingletonObjects中找BService —> 如果没有就创建BService的Bean对象
- 依赖注入,为cService属性赋值—>去单例池SingletonObjects中找CService —> 如果没有就创建CService的Bean对象
- 如果有其他属性就填充其他属性
- 初始化前操作 @PostConstruct
- 初始化 InitialzingBean接口中的afterPropertiesSet()
- 初始化后 AOF
- 放入单例池
BService 创建的生命周期
- 推断构造函数,创建BService的普通对象
- 依赖注入,为aService属性赋值—>去单例池中找AService对象 —>createSet找 —> 出现了循环依赖 —>从earlySingletonObjects中拿—>没有获取到,就提前AOP 生成AServiceProxy代理对象 —> 存入earlySingletonObjects<AService, AServiceProxy>
- 初始化前操作 @PostConstruct
- 初始化 InitialzingBean接口中的afterPropertiesSet()
- 初始化后 AOF
- 放入单例池
CService 创建的生命周期
- 推断构造函数,创建CService的普通对象
- 依赖注入,为aService属性赋值—>去单例池中找AService对象 —>createSet找 —> 出现了循环依赖 —>从earlySingletonObjects中拿,得到AServiceProxy代理对象
- 初始化前操作 @PostConstruct
- 初始化 InitialzingBean接口中的afterPropertiesSet()
- 初始化后 AOF
- 放入单例池
这里其实是用了三个map,也就是三级缓存,第一个就是单例池singletonObjects用来存放经历了完整Bean生命周期的单例bean,第二个就是earlySingletonObjects存放还没有经过完整Bean生命周期的单例bean,第三个map的用处 就是在BService的第二步中,对AService进行AOP操作是需要AService的普通对象的
Spring里面还是会用到一个map,我们可以暂时理解为前面说的hushangMap,先推断构造函数创建好AService的普通对象后,存入Map中去,key是bean的名字,value中存创建好的bean的名字、普通对象以及beanDefinition,这里有三个对象,value里面如何存放三个对象嘞?Spring是会去定义一个lambda表达式。
AService 创建的生命周期
- creatingSet(“AService”),把当前beanName存入一个set集合中
- 推断构造函数,创建AService的普通对象 —> singletonFactories<beanName, lambda(beanName,普通对象,beanDefinition)>
- 依赖注入,为bService属性赋值—>去单例池SingletonObjects中找BService —> 如果没有就创建BService的Bean对象
- 如果有其他属性就填充其他属性
- 初始化前操作 @PostConstruct
- 初始化 InitialzingBean接口中的afterPropertiesSet()
- 初始化后 AOF,假如第2步出现循环依赖已经进行了AOP操作,这里就不用再进行一遍了,如下图所示
- 如果出现了循环依赖,从二级缓存earlySingletonObjects中取代理对象/普通对象
- 放入单例池
BService 创建的生命周期
- 推断构造函数,创建BService的普通对象
- 依赖注入,为aService属性赋值—>去单例池中找AService对象 —> createSet找,判断是否出现了循环依赖 —> 出现了循环依赖 —>从earlySingletonObjects中拿—>如果没有获取到 ----> 从三级缓存singletonFactories找,得到lambda表达式,并执行lambda表达式,执行的方法中会去判断这Bean是否需要进行AOP—>AOP 生成AServiceProxy代理对象;如果不要进行AOP操作 lambda表达式就会得到一个普通对象 —> 存入earlySingletonObjects
- 初始化前操作 @PostConstruct
- 初始化 InitialzingBean接口中的afterPropertiesSet()
- 初始化后 AOF
- 放入单例池
总结:
最终的步骤如下:
AService 创建生命周期
- creatintSet(“AService”)
- 推断构造函数,实例化得到普通对象 —> singletonFactories(lombda(beanName,普通对象,beanDefinition))
- 依赖注入,为bService属性赋值—>去单例池SingletonObjects中找—>没找到 —> 创建BService的Bean对象
- 填充其他属性
- 初始化前操作,@PostConstruct
- 初始化,实现InitialozingBean接口,afterPropertiesSet()方法
- 初始化后,AOP操作,判断是否需要
- 将二级缓存earlySingletonObjects中的代理对象/普通对象取出来
- 存入单例池中
BService 创建生命周期
- 推断构造函数,实例化得到普通对象
- 依赖注入,为aService属性赋值 —> 去singletonObjects中找 —> 没找到 —> creatingSet,判断是否循环依赖 —> 二级缓存earlySingletonObjects中找 —> 没找到 —> singletonFactories --> 代理对象/普通对象 —> 存入二级缓存earlySingletonObjects
- 初始化前操作,@PostConstruct
- 初始化,实现InitialozingBean接口,afterPropertiesSet()方法
- 初始化后,AOP操作,判断是否需要
- 将二级缓存earlySingletonObjects中的代理对象/普通对象取出来
- 存入单例池中
版权归原作者 胡尚 所有, 如有侵权,请联系我们删除。