Spring中的循环依赖问题

在Java中,两个类之间是可以相互依赖的。比如A类实例化之后可以调用B类的方法,那么反之亦然。

如果不考虑Spring,循环依赖并不是问题,因为对象之间相互依赖是很正常的事情。

A a = new A();
B b = new B();
a.b = b;
b.a = a;

但是,在Spring当中,循环依赖就是一个问题。因为,在spring当中,一个对象并不是简单new出来的,而是经过了一系列的生命周期。

就是因为bean的生命周期所有才有了spring的循环依赖问题,在spring中,出现循环依赖的场景很多,有的场景spring自动帮我们解决了这个问题,而有时需要我们自己来解决。

下面,就以在aService中注入bService,bService注入aService为例来说明循环依赖的问题!!!

单例池
singletonObjects,底层实现是ConcurrentHashMap,key是bean的名字,value是bean对象,< beanName, bean对象 >

这样在getBean时获取的就是同一对象,因为beanName是唯一的(Map的特点)。

Spring中的循环依赖

这个过程涉及到了bean的生命周期,比如说,在aService中填充bService(@Autowired方式注入、DI依赖注入);

其实,就是从单例池中找到bService(不一定找到),如果直接找到就简单多了。如果找不到,就会创建bService(执行bService这个bean的生命周期);

bSerive又会找aService,此时aService还没有放入到单例池中,那么就会创建aService,之后又会找bService。就这样,反反复复,就是Spring的循环依赖问题。

Spring中的循环依赖

Spring如何解决这个循环

在一开始创建aService时,加入一层缓存map,Map< aService, aService对象 >,就可打破循环。

加入这个Map之后,如果在单例池中找不到bService的话,直接创建,然后执行bService的生命周期,这时会寻找aService,在单例池中它还是找不到的。但是,现在可以在Map中找到了,从而打破了循环。

问题1

特殊情况:这样的解决办法的确打破了循环,但还有一个问题。在bean的生命周期中,beanPostProcessor阶段,如果要加入后置通知(其实就是AOP),那么就会产生一个aService代理对象,最后放入单例池就应该是这个aService代理对象。但是呢,我们刚才加入的第二层缓存map使用的是实例化时的aService对象(不完整对象),我们需要使用的肯定是AOP的代理对象进行属性的注入,怎么解决这个问题呢?

方法:对aService提前进行AOP,只有当出现循环依赖问题时,才需要提前进行AOP。

如何判断是否出现循环依赖呢?创建一个set来记录,aService的bean生命周期过程中,如果需要创建aService,则判断为会出现循环依赖。只要创建aService,就往set中添加元素,set< aService >;当放入单例池后,set.remove。满足如下两个条件时,需要AOP提前:

  • 出现了循环依赖
  • 需要AOP

问题2

如果aService中在注入一个cService,那么就有两次调用代理对象aService了,但是因为要保证单例,这两个代理对象必须是同一个,那么就是用到二级缓存的时候了。

在放入单例池之前会执行一步操作,那就是从二级缓存中取出aService的代理对象,直接放入单例池中。

问题3

这样加入二级缓存后,如果不需要AOP,那么就不需要提前AOP操作了,这个问题怎么解决呢?核心是ObjectFactory、lambda表达式,更细节的优化就需要探索源码了!!!

三级缓存在实例化aService时就创建了,看源码可以知道创建了一个Bean 工厂,并将此工厂加入到三级缓存中。

总结

第一级缓存:单例池singletonObjects
ConcurrrentHashMap< beanName, 完整的bean对象>

第二级缓存:earlysingletonObjects
HashMap< beanName, 不完整的代理对象>

第三级缓存:singletonFactories
HashMap< beanName, ObjectFactory(lamda表达式)

源码中,addSingletonFactory(beanName, ()->getEarlyBeanReference(beanName, mbd, bean));

整个流程:(借鉴了简书的一篇详解)

1、A doCreateBean()初始化,由于还未创建,从一级缓存查不到,此时只是一个半成品(提前暴露的对象),放入三级缓存singletonFactories;
2、A发现自己需要B对象,但是三级缓存中未发现B,创建B的半成品,放入singletonFactories;
3、B发现自己需要A对象,从一级缓存singletonObjects和二级缓存earlySingletonObjects中未发现A,但是在三级缓存singletonFactories中发现A,将A放入二级缓存earlySingletonObjects,同时从三级缓存删除;
4、将A注入到对象B中;
5、B完成属性填充,执行初始化方法,将自己放入第一级缓存中(此时B是一个完整的对象);
6、A得到对象B,将B注入到A中;
7、A完成属性填充,初始化,并放入到一级缓存中。

在创建过程中,都是从三级缓存(对象工程创建不完整对象),将提前暴露的对象放入到二级缓存,从二级缓存拿到后,完成初始化,放入一级缓存。

后续仍需继续阅读源码,对spring的循环依赖问题进行探索!!!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值