总文档 :文章目录
Github : github.com/black-ant
一 . 前言
这是一篇尝试型的文章 , SpringIOC 的笔记基本上已经整理出来了 , 但是始终没有想好该怎么输出为文档 .
在我看来 , 如果我花了时间整理出一篇文档 , 那么不论多久 , 哪怕有所遗忘 , 后面再读的时候也应该能迅速读懂 . 整个 IOC 体系会以这个思路去输出 .
IOC 整个体系过于庞大 , 哪怕把之前的笔记全部梳理完 , 仍然感觉还是不够的 . 不过先挑个好说的 , 看看能不能先把这一部分梳理好 .
二 . 功能现象
循环依赖是指三个对象互相依赖 , 当通过 getBean 去获取依赖的 Bean 时 , 就形成了循环依赖
1 | JAVA复制代码 |
去掉 @Scope(value = “prototype”) 后 , 一切正常
循环依赖是在 单例模式中处理完成的
三 . 源码跟踪
我们从源码的角度分析一下 , SpringIOC 的单例是怎么控制循环依赖的
3.1 单例Bean 的加载流程
一切的起点
起点当然是从 AbstractBeanFactory 开始的 , 这一步会在 Bean 创建时详细说说 , 这里先放过
1 | java复制代码C- AbstractBeanFactory |
可以看到 , 当创建第一个 BeanA 时 , 进入 DefaultSingletonBeanRegistry
1 | java复制代码// 继续深入 DefaultSingletonBeanRegistry , 其中有以下的参数 |
怎么创建一个单例Bean
1 | java复制代码// 当我们创建一个单例Bean 时 |
从整个流程来看 , 最主要的就是从三个 CurrentHashMap 本地缓存中获取 Bean 实例
PS : 这里可以预先说一下 , 当第一个Bean 创建时 , 就已经在缓存总存在了 , 哪怕他还没有完全加载完成
BeanABeanBBeanCsingletonObjectsearlySingletonObjectssingletonFactoriesGet(B) 获取 BeanBGet(C) 获取 BeanCGet(A) 获取 BeanA从singletonObjects 中第一次获取获取 BeanA获取失败 ,且已创建从缓存中Get(A) 获取 BeanA获取失败 ,且需要提前创建从缓存中Get(A) 获取 BeanABeanABeanBBeanCsingletonObjectsearlySingletonObjectssingletonFactories
我们就这几个缓存来好好的看一下 :
继续向下检索 ,看一下 上面2个对象是什么时候放上去的
1 | java复制代码// 着重复习一下上面几个对象 |
3.2 singletonsCurrentlyInCreation
Step 1 : singletonsCurrentlyInCreation 插入的地方
1 | java复制代码C180- DefaultSingletonBeanRegistry |
3.3 singletonObjects 的存储
singletonObjects 存入的位置
1 | java复制代码M180_05- addSingleton |
PS : 使用该对象作为 synchronized 的监视对象可以有效保证唯一性
一开始我在想 ConcurrentHashMap 应该也可以保证并发唯一 , 后来想了一下 , 这里的唯一应该是业务流程上的唯一 ,例如我这里要删除 , 你那里就不能进行添加 , 等我做完了 , 你才能继续
singletonObjects 删除的地方
1 | java复制代码 |
PS : 也就是说 , 销毁单例时 , 该数据会被清空或者移除
1 | java复制代码// M180_07 代码 |
addSingletonsingletonObjectsclearSingletonCacheremoveSingletonsingletonObjects 集合addSingletonFactory 时 put进入参数clearSingletonCache 方法中移除removeSingleton 方法中移除addSingletonsingletonObjectsclearSingletonCacheremoveSingleton
3.4 earlySingletonObjects
earlySingletonObjects 保存的地方
1 | java复制代码C180- DefaultSingletonBeanRegistry |
PS : 这个的位置为 初始类 BeanA 加载时 , 此时未注入依赖 , 只是第一次构建
earlySingletonObjects 是一个中间缓存 , 在Single Bean 添加完成后 , 这个中间缓存就会被清除
earlySingletonObjects 清除的地方
1 | java复制代码C180- DefaultSingletonBeanRegistry |
getSingletonearlySingletonObjectsaddSingletonFactoryclearSingletonCacheaddSingletonearlySingletonObjects 集合getSingleton 时 put进入参数addSingletonFactory 方法中移除clearSingletonCache 方法中 clearaddSingleton 方法中 移除getSingletonearlySingletonObjectsaddSingletonFactoryclearSingletonCacheaddSingleton
3.5 singletonFactories
singletonFactories 是单例工厂
1 | java复制代码// 还是先看下 |
PS : 该对象会在后续中被 getSingleton 获取继续处理
DefaultSingletonBeanRegistrysingletonFactoriesgetSingletonremoveSingletonclearSingletonCachesingletonFactories 集合addSingletonFactory 时 put进入参数singletonFactories 方法中移除removeSingleton 方法中移除clearSingletonCache 方法中 clearDefaultSingletonBeanRegistrysingletonFactoriesgetSingletonremoveSingletonclearSingletonCache
3.6 为什么 prototype 不可行
这里涉及到 prototype 的加载过程 :
prototype 的创建是在 AbstractBeanFactory 中完成
1 | java复制代码C171- AbstractBeanFactory |
完整流程
BeanCsingletonObjectsearlySingletonObjectssingletonFactories从singletonObjects 中第一次获取获取 BeanA获取失败 ,且已创建从缓存中Get(A) 获取 BeanA获取失败 ,且需要提前创建从缓存中Get(A) 获取 BeanABeanCsingletonObjectsearlySingletonObjectssingletonFactories
补充资料 : SingletonBeanRegistry 体系
总结
循环依赖的处理流程其实很简单 , 篇幅也不长 , 主要就是几个缓存集合的使用.
这篇主要是想看看能不能使文档具有快速会议的能力
参考文献
附录
本文转载自: 掘金