前言
昨天我们说了什么是spring的循环依赖,以及产生的原因,今天那我们就来说说如何解决spring的循环依赖问题。 上篇文章说到过,只有通过setter方法进行依赖注入且是在单例模式下产生的循环依赖问题是被解决的?
Spring是怎样解决单例模式下循环依赖的呢?
其实主要的就是靠提前暴露创建中的单例实例。
单例模式下的Setter赋值的循环依赖
如图的例子,A依赖B,B依赖C,C又依赖B。
其过程过程如下:
- 创建A,调用构造方法并完成构造,进行属性赋值注入,发现了依赖B,去实例化B。
- 创建B,调用构造方法并完成构造,进行属性赋值注入,发现了依赖C,去实例化C。
- 创建C,调用构造方法并完成构造,进行属性赋值注入,发现依赖A。
此时就是解决循环依赖的关键,因为A已经通过构造方法已经构造完成了,也就是说已经将Bean在堆中分配好了内存,这样即使A再填充属性值其也不会在改变它的内存地址了,所以此时就可以提前拿出来A的引用,来完成对C的实例化。
所以通过使用这种方法,对c的创建过程就变成: 4. 创建C,调用构造方法,完成构造并进行属性赋值注入,发现了依赖A,A也已经构造完成,直接引用,完成C的实例化。 5. C完成实例化后,注入B,B也完成了实例化,然后B注入A,A也完成了实例化。
为了能获取到创建中单例bean,spring就提供了三级缓存来将正在创建中的bean提前暴露,从而解决单例模式下的Setter赋值的循环依赖问题。 过程如图所示:
Spring三大缓存:
Spring中有三个缓存,缓存的作用是用来存储单例的Bean实例;这三个缓存是彼此互斥的,不会针对同一个Bean的实例同时进行存储。
如果调用getBean,则需要从三个缓存中按顺序依次进行获取指定的Bean实例。其读取顺序依次是:
一级缓存-->二级缓存-->三级缓存
- 一级缓存,
singletonObjects
单例缓存,存储已经实例化的单例bean(已经创建完毕)。 - 二级缓存,
earlySingletonObjects
提前暴露的单例缓存,其存储的bean是刚刚构造完成,但后面还会通过属性注入bean(Bean被提前暴露的引用,但是Bean还在创建中)。 - 三级缓存,
singletonFactories
生产单例的工厂缓存,存储工厂(正在创建中)。
总结
通过spring的三个缓存方式来解决单例模式下循环依赖的问题,而其他的场景下产生的依赖问题是解决不了的,会报错。