你真的懂 Spring 解决循环依赖吗?
前言
我们经常会被问到一个问题:Spring 是如何解决循环依赖的问题的。这个问题算是关于 Spring 的一个高频面试题,因为如果不刻意研读,相信即使读过源码,面试者也不一定能够一下子思考出个中奥秘。
本文主要针对这个问题,从源码的角度对其实现原理进行讲解。
一、什么是循环依赖
多个 bean 之间相互依赖,形成了一个闭环。 比如:A 依赖于 B、B 依赖于 c、c 依赖于 A
通常来说,如果问 spring 容器内部如何解决循环依赖, 一定是指默认的单例 Bean 中,属性互相引用的场景。也就是说,Spring 的循环依赖,是 Spring 容器注入时候出现的问题。
二、Spring 如何解决循环依赖
1、Spring 中单例 Bean 的三级缓存
第一级缓存〈也叫单例池)singletonObjects:存放已经经历了完整生命周期的 Bean 对象
第二级缓存: earlySingletonObjects,存放早期暴露出来的 Bean 对象,Bean 的生命周期未结束(属性还未填充完整)
第三级缓存: Map<String, ObiectFactory<?>> singletonFactories,存放可以生成 Bean 的工厂
2、Spring 中 Bean 的生命周期
3、Bean 初始化主要方法
getSingleton:希望从容器里面获得单例的 bean,没有的话
doCreateBean: 没有就创建 bean
populateBean: 创建完了以后,要填充属性
addSingleton: 填充完了以后,再添加到容器进行使用
4、具体说明
A 创建过程中需要 B,于是 A 将自己放到三级缓存里面,去实例化 B
B 实例化的时候发现需要 A,于是 B 先查一级缓存,没有,再查二级缓存,还是没有,再查三级缓存,找到了 A 然后把三级缓存里面的这个 A 放到二级缓存里面,并删除三级缓存里面的 A
B 顺利初始化完毕,将自己放到一级缓存里面(此时 B 里面的 A 依然是创建中状态)然后回来接着创建 A,此时 B 已经创建结束,直接从一级缓存里面拿到 B,然后完成创建,并将 A 放到一级缓存中。
5、图解
三、为什么使用三级缓存
1、使用一级缓存
实例化 A->将半成品的 A 放入 singletonObjects 中->填充 A 的属性时发现取不到 B->实例化 B->从 singletonObjects 中取出 A 填充 B 的属性->将成品 B 放入 singletonObjects->将 B 填充到 A 的属性中->将成品 A 放入 singletonObjects。
问题:这种基本流程是通的,但是如果在整个流程进行中,有另一个线程要来取 A,那么有可能拿到的只是一个属性都为 null 的半成品 A,这样就会有问题。
2、使用二级缓存
a)使用 singletonObjects 和 earlySingletonObjects
成品放在 singletonObjects 中,半成品放在 earlySingletonObjects 中
流程可以这样走:实例化 A ->将半成品的 A 放入 earlySingletonObjects 中 ->填充 A 的属性时发现取不到 B->实例化 B->将半成品的 A 放入 earlySingletonObjects 中->从 earlySingletonObjects 中取出 A 填充 B 的属性->将成品 B 放入 singletonObjects,并从 earlySingletonObjects 中删除 B->将 B 填充到 A 的属性中->将成品 A 放入 singletonObjects 并删除 earlySingletonObjects。
问题:这样的流程是线程安全的,不过如果 A 上加个切面(AOP),这种做法就没法满足需求了,因为 earlySingletonObjects 中存放的都是原始对象,而我们需要注入的其实是 A 的代理对象。
b)使用 singletonObjects 和 singletonFactories
成品放在 singletonObjects 中,半成品通过 singletonFactories 来获取
流程是这样的:实例化 A ->创建 A 的对象工厂并放入 singletonFactories 中 ->填充 A 的属性时发现取不到 B->实例化 B->创建 B 的对象工厂并放入 singletonFactories 中->从 singletonFactories 中获取 A 的对象工厂并获取 A 填充到 B 中->将成品 B 放入 singletonObjects,并从 singletonFactories 中删除 B 的对象工厂->将 B 填充到 A 的属性中->将成品 A 放入 singletonObjects 并删除 A 的对象工厂。
问题:这样的流程也适用于普通的 IOC,但如果 A 上加个切面(AOP)的话,这种情况也无法满足需求,因为每次通过 singletonFactories.getObject()获取的代理对象都不同。
作者:Java 小叮当
链接:https://juejin.cn/post/6939741610116644878
评论