1.什么是循环依赖
比如,A对象依赖了B对象,而B对象又依赖了A对象。
@Component
public class A {
@Autowired
private B b ;
public void fn(){
System.out.println("Class A fn() is called...");
}
public void callOtherFn(){
b.fn();
}
}
@Component
public class B {
@Autowired
private A a;
public void fn(){
System.out.println("Class B fn() is called...");
}
public void callOtherFn(){
a.fn();
}
}
如果不考虑Spring,循环依赖并不是问题,因为对象之间相互依赖是很正常的事情。也可以使用new关键字正常地创建对象即可。但是,在Spring中循环依赖就是一个问题了,因为,在Spring中,一个对象并不是简单new出来了,而是会经过一系列的Bean的生命周期,最终由IOC容器注入,因此就会出现循环依赖问题。
2.如何解决循环依赖
Spring通过三级缓存来解决循环依赖问题,首先看下三级缓存是通用的说法。 - 一级缓存为:singletonObjects - 二级缓存为:earlySingletonObjects - 三级缓存为:singletonFactories
解决思路: A创建时--->需要B---->B去创建--->需要A,从而产生了循环依赖。如下图:

那么如何打破这个循环,加个中间人(缓存)

A的Bean在创建过程中,在进行依赖注入之前,先把A的原始Bean放入缓存(提早暴露,只要放到缓存了,其他Bean需要时就可以从缓存中拿了,称之为:earlySingletonObjects),放入缓存后,再进行依赖注入,此时A的Bean依赖了B的Bean,如果B的Bean不存在,则需要创建B的Bean,而创建B的Bean的过程和A一样,也是先创建一个B的原始对象,然后把B的原始对象提早暴露出来放入缓存中,然后在对B的原始对象进行依赖注入A,此时能从缓存中拿到A的原始对象(虽然是A的原始对象,还不是最终的Bean),B的原始对象依赖注入完了之后,B的生命周期结束,那么A的生命周期也能结束。 因为整个过程中,都只有一个A原始对象,所以对于B而言,就算在属性注入时,注入的是A原始对象,也没有关系,因为A原始对象在后续的生命周期中在堆中没有发生变化。
从上面这个分析过程中可以得出,只需要一个缓存就能解决循环依赖了,那么为什么Spring中还需要singletonFactories呢?
这是难点,基于上面的场景想一个问题:如果A的原始对象注入给B的属性之后,A的原始对象进行了AOP产生了一个代理对象,此时就会出现,对于A而言,它的Bean对象其实应该是AOP之后的代理对象,而B的a属性对应的并不是AOP之后的代理对象,这就产生了冲突。因此出现了B依赖的A和最终的A不是同一个对象。
为了解决这个问题,因此出现了三级缓存singletonFactories,这个工厂的作用就是判断这个对象是否需要代理。
3.总结
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了。