我们以上述代码为例分析一下死循环产生的过程
为什么A是半成品呢?
如果熟悉bean的生命周期,那么在实例化对象A的时候,首先去调用的是构造函数,像是依赖注入还有接口的实现重写什么的,还有后置处理器,初始化方法都还没有执行
如图,然后发生了死循环,或者说是循环依赖
Spring三级缓存解决循环依赖
//单实例对象注册器
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
private static final int SUPPRESSED_EXCEPTIONS_LIMIT = 100;
private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap(16);
}
缓存名称 | 源码名称 | 作用 |
一级缓存 | singletonObjects | 单例池,缓存已经经历了完整的生命周期,初始化完成的bean对象 |
二级缓存 | earlySingletonObjects | 缓存早期的bean对象(生命周期还没走完) |
三级缓存 | singletonFactories | 缓存的是ObjectFactory,表示对象工厂,用来创建某个对象的 |
一级缓存可以解决循环依赖吗?
限制bean在beanFactory中只存一份,即实现singleton scope,解决不了循环依赖
一级缓存和二级缓存一起可以解决循环依赖吗?
如果要想打破循环依赖, 就需要一个中间人的参与, 这个中间人就是二级缓存。
一级缓存和二级缓存可以解决一般对象的循环依赖,但是如果是被增强的对象(代理对象),那么最终存到的并不是代理对象,这时就需要三级缓存
注意:由于是单例模式,所以即使首先注入的是半成品A,那么后续A进行初始化后,B中的半成品A就是完整的A了
三级缓存可以解决循环依赖吗?
如果是一般对象,对象工厂就会创建一般对象,而如果是代理对象,对象工厂就会帮你创建代理对象
注意:其实本质上来说,3级缓存缓存的就是对象的地址,通过把地址给B对象来走完B的流程
通过三级缓存可以解决大部分的循环引用问题,少部分需要我们手动解决,比较典型的就是构造方法的循环注入
构造方法出现了循环依赖怎么解决?
与上面最大的区别就是,上面的问题是执行完构造方法,也就是对象已经被创建成功了。而现在是通过构造器注入
如图,这是bean的生命周期,三级缓存可以解决初始化过程中的循环依赖,而无法解决构造函数产生的循环依赖,那应该如何解决呢?
什么时候需要对象,我再进行bean对象的创建,并不是在实例化的时候,直接把对象注入进来,什么时候用,什么时候实例化,这样就能解决构造方法产生循环依赖的问题
面试回答
面试官:Spring中的循环引用
候选人: 嗯,好的,我来解释一下 循环依赖:循环依赖其实就是循环引用,也就是两个或两个以上的bean互相持 有对方,最终形成闭环。比如A依赖于B,B依赖于A 循环依赖在spring中是允许存在,spring框架依据三级缓存已经解决了大部 分的循环依赖
①一级缓存:单例池,缓存已经经历了完整的生命周期,已经初始化完成的 bean对象 ②二级缓存:缓存早期的bean对象(生命周期还没走完) ③三级缓存:缓存的是ObjectFactory,表示对象工厂,用来创建某个对象的
面试官:那具体解决流程清楚吗?
候选人: 第一,先实例A对象,同时会创建ObjectFactory对象存入三级缓存 singletonFactories
第二,A在初始化的时候需要B对象,这个走B的创建的逻辑。
第三,B实例化完成,也会创建ObjectFactory对象存入三级缓存 singletonFactories
第四,B需要注入A,通过三级缓存中获取ObjectFactory来生成一个A的对象 同时存入二级缓存,这个是有两种情况,一个是可能是A的普通对象,另外 一个是A的代理对象,都可以让ObjectFactory来生产对应的对象,这也是三 级缓存的关键
第五,B通过从通过二级缓存earlySingletonObjects 获得到A的对象后可以正 常注入,B创建成功,存入一级缓存singletonObjects
第六,回到A对象初始化,因为B对象已经创建完成,则可以直接注入B,A 创建成功存入一次缓存singletonObjects
第七,二级缓存中的临时对象A清除
面试官:构造方法出现了循环依赖怎么解决?
候选人: 由于bean的生命周期中构造函数是第一个执行的,spring框架并不能解决构 造函数的的依赖注入,可以使用@Lazy懒加载,什么时候需要对象再进行 bean对象的创建