在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如何解决这个循环
在一开始创建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的循环依赖问题进行探索!!!