写点什么

你真的懂 Spring 解决循环依赖吗?

用户头像
云流
关注
发布于: 2021 年 03 月 19 日

前言

我们经常会被问到一个问题: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


用户头像

云流

关注

还未添加个人签名 2020.09.02 加入

还未添加个人简介

评论

发布
暂无评论
你真的懂Spring解决循环依赖吗?