如何解决 spring 的循环依赖问题?
昨天我们说了什么是 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 的三个缓存方式来解决单例模式下循环依赖的问题,而其他的场景下产生的依赖问题是解决不了的,会报错。
作者:Ahai
链接:https://juejin.cn/post/7222310732292489275
来源:稀土掘金
评论