Spring IOC 源码简单分析 03 - 循环引用
### 准备
## 目标
##测试代码

</beans>
### 分析
## 问题何在
按照前一篇 getBean 方法流程分析的结论,当第20行代码通过 getBean 方法获取 chairman bean 实例时,会先实例化 gordon.study.spring.common.Employee 实例,然后装配 name 和 company 属性,由于 company 属性值是另一个 bean macrohard,因此会调用 getBean 方法获取 macrohardbean 实例,巧合的是,macrohard bean 的 employees 属性又依赖 chairman bean 实例,形成循环引用。
## 解决思路
## 源码分析

- 调用 getSingleton(String beanName) 方法尝试获取已构建完成的 bean 实例,或者在发现循环引用时获取 early singleton bean 实例。
- 如果前一步 getSingleton 方法没有找到合适的 bean 实例,则通过 getSingleton(String beanName, ObjectFactory<?> singletonFactory) 方法创建 bean 实例。该方法调用过程中会引起递归(创建 bean)。
- 尝试直接从 singletonObjects 中获取已构建完成的 bean 实例。singletonObjects 用于缓存已完全构建的 bean 实例。
- 在 beforeSingletonCreation 方法中将 bean name 放入集合 singletonsCurrentlyInCreation 中。singletonsCurrentlyInCreation 记录已经在创建中的所有单例 bean,当相同的 bean 妄图进入该集合时,表示发现循环引用且无法解决,将抛出 BeanCurrentlyInCreationException 异常。
- 调用参数 ObjectFactory 的 getObject 方法创建 bean 实例。
getObject 方法直接调用 AbstractAutowireCapableBeanFactory 的 createBean 方法创建 bean 实例,包括实例化、属性装配与初始化工作,调用栈如下图所示(截图来自上一篇文章):
在本例中,因为使用的都是无参构造函数,所以实例化工作是一定可以完成的。当实例化完成后,有以下与循环引用相关的代码:

默认情况下,全局参数允许循环引用(allowCircularReference)值为 true,考虑到当前 bean 在上一步一定已经放入 singletonsCurrentlyInCreation 集合,因此会调用 addSingletonFactory 方法为该 bean 创建对应的 ObjectFactory。【(以下描述不准确)addSingletonFactory方法的实现有点闭包的味道:实际上是建立了 beanName 与 getEarlyBeanReference 方法在当时的上下文环境(可以简单理解为局部变量值环境)下的联系。之后需要获取 beanName 的 early bean reference 时,通过调用 ObjectFactory 的 getObject 方法,相当于调用当时上下文环境下的 getEarlyBeanReference方法(参数值保留了创建 ObjectFactory 实例时上下文环境中的变量值)。可以想象,如果不用闭包,需要将 mbd 与 bean 用其它的方式与 beanName 建立对应关系并缓存起来。实际上对于本例,getEarlyBeanReference方法返回的就是其参数 bean,也就是此段代码前面刚刚创建的 bean 实例。】
如上,bean name 与相应的 ObjectFactory 被放入 singletonFactories 中。同时可以发现,当 beanName 存在于 singletonFactories中时,一定不存在于 earlySingletonObjects 中。
之后,在装配属性时可能会依赖其它 bean 实例,通过 getBean 方法获取相应的 bean 实例。如果没有循环引用,则形成典型的递归调用(getBean -> 方法二 -> getBean);如果发现循环引用,则方法一介入,见下文分析。 - 在 afterSingletonCreation 方法中将 bean name 从 singletonsCurrentlyInCreation 中移除。
- 在 addSingleton 方法中将创建出的实例放入 singletonObjects 中。
第147行将 bean name 从 earlySingletonObjects 中移除。目前看来纯粹是为了释放内存,并无其它深意。
- 尝试直接从 singletonObjects 中获取已装配完成的 bean 实例
- 如果 bean name 在集合 singletonsCurrentlyInCreation 中,表示发现循环引用。此时,本方法会通过 earlySingletonObjects 要么返回已经创建好的 early singleton bean 实例,要么通过 singletonFactories 找到对应的 ObjectFactory 创建 early singleton bean 实例并放入 earlySingletonObjects 中。

## 本例分析
- 对于本例,首先通过 getBean 方法获取 chairman bean,getSington 方法一返回 null(因为 singletonsCurrentlyInCreation 当前为空,不包含 chairman),getSington 方法二实例化 chairman bean,在装配 company 属性时,需要构造 macrohard bean。此时 singletonObjects 为空,earlySingletonObjects 为空,singletonsCurrentlyInCreation 包含 chairman,singletonFactories 包含 chairman。
- 递归调用 getBean 方法获取 macrohard bean,getSington 方法一返回 null(因为singletonsCurrentlyInCreation当前不包含 macrohard),getSington 方法二实例化
macrohard bean,在装配 employees 属性时,需要构造 chairman bean。此时 singletonObjects 为空,earlySingletonObjects 为空,singletonsCurrentlyInCreation 包含 chairman 与 macrohard,singletonFactories 包含 chairman 与 macrohard。 - 再次递归进入 getBean 方法获取 chairman bean,方法一创建 early singleton bean chairman 并放入 earlySingletonObjects。此时 singletonObjects 为空,earlySingletonObjects 包含 chairman,singletonsCurrentlyInCreation 包含 chairman 与 macrohard,singletonFactories 包含 macrohard。
- 返回 macrohard bean 构建过程,完成构建。此时 singletonObjects 包含 marcohard,earlySingletonObjects 包含 chairman,singletonsCurrentlyInCreation 包含 chairman,singletonFactories 为空。
- 返回 chairman bean 构建过程,完成构建。此时 singletonObjects 包含 marcohard 与 chairman,earlySingletonObjects 为空,singletonsCurrentlyInCreation 为空,singletonFactories 为空。
## 其它循环引用情况分析
通过 setter 注入方式产生的循环引用是可以通过以上方案解决的。
Spring IOC 源码简单分析 03 - 循环引用的更多相关文章
- Spring IOC 源码简单分析 01 - BeanFactory
### 准备 ## 目标 了解 Spring IOC 的基础流程 ## 相关资源 Offical Doc:http://docs.spring.io/spring/docs/4.3.9.RELEASE ...
- Spring IOC 源码简单分析 02 - Bean Reference
### 准备 ## 目标 了解 bean reference 装配的流程 ##测试代码 gordon.study.spring.ioc.IOC02_BeanReference.java ioc02 ...
- Spring IOC 源码简单分析 04 - bean的初始化
### 准备 ## 目标 了解 Spring 如何初始化 bean 实例 ##测试代码 gordon.study.spring.ioc.IOC04_Initialization.java publ ...
- Spring Ioc源码分析系列--自动注入循环依赖的处理
Spring Ioc源码分析系列--自动注入循环依赖的处理 前言 前面的文章Spring Ioc源码分析系列--Bean实例化过程(二)在讲解到Spring创建bean出现循环依赖的时候并没有深入去分 ...
- Spring IOC 源码分析
Spring 最重要的概念是 IOC 和 AOP,本篇文章其实就是要带领大家来分析下 Spring 的 IOC 容器.既然大家平时都要用到 Spring,怎么可以不好好了解 Spring 呢?阅读本文 ...
- spring IoC源码分析 (3)Resource解析
引自 spring IoC源码分析 (3)Resource解析 定义好了Resource之后,看到XmlFactoryBean的构造函数 public XmlBeanFactory(Resource ...
- Spring Ioc源码分析系列--Ioc源码入口分析
Spring Ioc源码分析系列--Ioc源码入口分析 本系列文章代码基于Spring Framework 5.2.x 前言 上一篇文章Spring Ioc源码分析系列--Ioc的基础知识准备介绍了I ...
- Spring Ioc源码分析系列--Ioc容器BeanFactoryPostProcessor后置处理器分析
Spring Ioc源码分析系列--Ioc容器BeanFactoryPostProcessor后置处理器分析 前言 上一篇文章Spring Ioc源码分析系列--Ioc源码入口分析已经介绍到Ioc容器 ...
- Spring Ioc源码分析系列--Bean实例化过程(一)
Spring Ioc源码分析系列--Bean实例化过程(一) 前言 上一篇文章Spring Ioc源码分析系列--Ioc容器注册BeanPostProcessor后置处理器以及事件消息处理已经完成了对 ...
随机推荐
- 使用Yii2中dropdownlist实现地区三级联动的例子
原文:http://www.yiichina.com/code/636 <?php use yii\helpers\Url; use yii\widgets\ActiveForm; use yi ...
- 02Del.ashx(删除班级)
using System; using System.Collections.Generic; using System.Linq; using System.Web; using WebHelper ...
- oracle基础开发工具及常用命令
oracle自带用户的讲解 1,oracle安装完毕创建数据库实例的时候会自动生成sys,system,scott 2,sys用户超级管理员,具有oracle的最高权限,具有sysdba角色,具有cr ...
- centos7 iptables/firewalld docker open port
here are multiple "hackish" ways to do it: scan kernel logs, as mentioned by Jiri (but you ...
- webView返回不刷新
[[NSUserDefaults standardUserDefaults] setInteger:2 forKey: @"WebKitCacheModelPreferenceKey&quo ...
- Shape of passed values is (3490, 21), indices imply (3469, 21)
背景 处理DataFrame数据时,抛了这个错误:Shape of passed values is (3490, 21), indices imply (3469, 21) 解决 数据出现重复,导致 ...
- HDFS的Java API
HDFS Java API 可以用于任何Java程序与HDFS交互,该API使我们能够从其他Java程序中利用到存储在HDFS中的数据,也能够使用其他非Hadoop的计算框架处理该数据 为了以编程方式 ...
- python3 os.walk()使用
os.walk() 方法用于通过在目录树种游走输出在目录中的文件名,向上或者向下. 在Unix,Windows中有效. os.walk(top[, topdown=True[, onerror=Non ...
- hdu 5103 状态压缩dp
这题说的是给了n(14)个点,每个点都以他 为根的最大可容的孩子个数和最小的可溶孩子个数L[i] ,R[i] 问这n个点形成一棵树有多少种形态 我们让 dp[i][S] 表示 一 i为根节点 的 拥有 ...
- webpack踩过的坑(总结)
使用process.argv 获取命令行使用的参数 // 判断是否带production参数,production会压缩js var isprod = false; for (var i in pro ...