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后置处理器以及事件消息处理已经完成了对 ...
随机推荐
- 百度地图API开发----手机地图做导航功能
第一种方式:手机网页点击打开直接进百度地图APP <a href="baidumap://map/direction?mode=[transit:公交,driving:驾车]& ...
- Oracle自动备份脚本的实现
问题描述: Oracle自动备份脚本的实现. 错误提示1: Message file RMAN.msb not found Verify that Oracle_HOME is set properl ...
- Docker容器之Nginx
一,pull一个Nginx镜像 docker pull nginx 二,Nginx镜像文件说明 配置文件 /etc/nginx/nginx.conf 网站根目录 /usr/share/nginx/ht ...
- 虚拟机中CentoOs配置ip且连网
1.修改"VMware Network Adapter VMnet8",配置IP 2.打开虚拟机,"编辑" => "虚拟网络编辑器", ...
- Log4j最简入门及实例
Log4j真的很简单,简单到令人发指的地步.不是要记录日志吗?那就给你一个Log,然后你用Log来写东西就行了,先来一个完整类示例: package test; import org.apache.c ...
- ora-28056错误解决
问题描述:今天有同事找我,说是oracle数据库的监听器出现问题,我连接服务器查看后,发现不是监听器问题,而是进程连接数已经达到150个了,客户端连接不上服务器,因其是测试服务器,重启服务器后再次sq ...
- 过千万、亿条数据的mysql表更新 mysql 线程状态
分段更新 UPDATE question SET `status`=1 WHERE status!=1 LIMIT 3000;UPDATE answer SET `status`=1 WHERE st ...
- JAVA参数没有引用传递,只有值传递
原文章地址:http://www.cnblogs.com/clara/archive/2011/09/17/2179493.html 当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性, ...
- Windows环境下手动更新boot2docker.iso
GitHub连不上导致自动更新失败. https://github.com/boot2docker/boot2docker/releases 替换了DockerToolbox安装目录和系统盘用户目录\ ...
- Navicat运行sql文件报错out of memory
下载并安装mysql workbench: