前言:在doCreateBean方法中还遗留一个问题没有分析:循环依赖。循环依赖在Spring中是非常重要的一个知识点,因此单独进行分析。


什么是循环依赖

循环依赖就是循环引用,两个或两个以上的bean互相引用对方,最终形成一个闭环。如A依赖B,B依赖C,C依赖A。如下图所示:

循环依赖其实就是一个死循环的过程,在初始化A的时候发现引用了B,则就会去初始化B,然后发现B又引用C,则又去初始化C,在初始化C的时候,再次发现C引用了A,则又去初始化A,这样就处于死循环,除非有终结条件。

Spring中循环依赖的场景有两种:

  • 构造器循环依赖。
  • setter循环依赖。

构造器循环依赖

表示通过构造器注入构成的循环依赖,此依赖是无法解决的,只能抛出BeanCurrentlyInCreationException异常。

setter循环依赖

表示通过setter注入方式构成的循环依赖,对于setter注入造成的循环依赖,Spring只解决单例模式下的循环依赖,对于其他作用域的循环依赖,则抛出BeanCurrentlyInCreationException异常。为什么Spring只解决单例模式下的循环依赖呢,这里首先了解下Spring的几个作用域。

Spring的scope作用域

  • singleton:默认的scope,每个scope是singleton的bean都会被定义一个单例对象,该对象的生命周期与Spring IOC容器一致的(但在第一次注入时才会创建)。
  • prototype:如果为bean的作用域为prototype,则每次注入时都会创建一个新的对象。
  • request:bean被定义为在每个HTTP请求 中创建一个单例对象,也就是说在单个请求中都会复用这个单例对象。
  • session:bean被定义为在一个session的生命周期内创建一个单例对象。
  • application:bean被定义为在ServletContext的生命周期中复用一个单例对象。
  • websocket:bean被定义为在websocket的生命周期中复用一个单例对象。

从上面对Spring中scope作用域的介绍,也可大致了解为什么Spring中只解决单例模式下的循环依赖了,因为其他作用域对象的生命周期并不与Spring IOC容器一致,并且最主要的一点是Spring并不会对除了单例模式的bean做缓存,因此Spring只能解决单例模式下的循环依赖。

具体循环依赖解决流程

首先在看bean的加载的入口,在doGetBean方法中有如下代码:

首先会根据beanName从单例bean缓存中获取,如果不为空,则直接返回,前面已经分析了该方法,这里再次提出来。

 // DefaultListableBeanFactory
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 从单例缓存中加载Bean
Object singletonObject = this.singletonObjects.get(beanName);
// 缓存中bean为空,且当前bean正在创建
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 做同步
synchronized (this.singletonObjects) {
// 从earlySingletonObjects集合中获取
singletonObject = this.earlySingletonObjects.get(beanName);
// earlySingletonObjects集合中没有,其允许提前创建
if (singletonObject == null && allowEarlyReference) {
// 从singletonFactories中获取对应的ObjectFactory
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// 获取bean
singletonObject = singletonFactory.getObject();
// 将bean添加到earlySingletonObjects集合中
this.earlySingletonObjects.put(beanName, singletonObject);
// 从singletonFactories中移除对应的
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}

分析:

这里注意isSingletonCurrentlyInCreation函数,判断当前bean是否正在被创建,这里就是判断beanName是否在singletonsCurrentlyInCreation集合中,那么正在被创建的bean是合适添加进去的呢?还是在doGetBean方法中

 // DefaultSingletonBeanRegistry
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
// 做同步
synchronized (this.singletonObjects) {
// 从缓存中检查一遍
// 因为singlton模式其实已经复用了创建的bean,所以该步骤必须检查
Object singletonObject = this.singletonObjects.get(beanName);
// 为空,开始进行加载
if (singletonObject == null) {
if (this.singletonsCurrentlyInDestruction) {
throw new BeanCreationNotAllowedException(beanName,
"Singleton bean creation not allowed while singletons of this factory are in destruction " +
"(Do not request a bean from a BeanFactory in a destroy method implementation!)");
}
if (logger.isDebugEnabled()) {
logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
}
// 加载前置处理 其实就是打一个标记
beforeSingletonCreation(beanName);
// 首先将新的newSingleton设置为false
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
// 初始化bean
// 该过程其实是调用createBean()方法 这里是一个回调方法
singletonObject = singletonFactory.getObject();
newSingleton = true;
} catch (IllegalStateException ex) {
// Has the singleton object implicitly appeared in the meantime ->
// if yes, proceed with it since the exception indicates that state.
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
throw ex;
}
} catch (BeanCreationException ex) {
if (recordSuppressedExceptions) {
for (Exception suppressedException : this.suppressedExceptions) {
ex.addRelatedCause(suppressedException);
}
}
throw ex;
} finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
// 一堆异常处理后,进行后置处理 移除标志
afterSingletonCreation(beanName);
}
// 新的bean 加入缓存中
if (newSingleton) {
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}

分析:

注意第20行代码beforeSingletonCreation函数。

 // DefaultSingletonBeanRegistry
protected void beforeSingletonCreation(String beanName) {
// 这里会添加到正在创建bean的集合中
// 注意第一个条件,如果存在,则为false,直接短路
// 只有当第一个条件不存在[false]时,才会去进行添加操作
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}

分析:

这里就会将正在创建的beanName添加到singletonsCurrentlyInCreation,如果出现构造函数的循环依赖bean注入,则会在此处抛出BeanCurrentlyCreationException异常,具体可以跟着代码调试一把,就可以更清楚的知晓流程。

接下来继续回到单例模式的循环依赖中:

在加载bean时,首先从单例缓存中获取bean对象。

  • 首先从单例缓存中获取bean对象,如果缓存中存在bean对象则直接返回(单例模式的bean在创建过程中会进行缓存[singletonObjects])。
  • 如果缓存中bean对象为空,且当前bean正在创建,则从earlySingletonObjects中获取。
  • 如果earlySingletonObjects集合中不存在,且允许提前创建bean,则从singletonFactories中获取单例工厂,若singleFactory不为空,则通过getObject方法获取bean,并将bean对象加入到earlySingletonObjects集合中,然后从singleFactory集合中移除对应的单例工厂对象。

注意这里涉及到三个集合:

  • singletonObjects (一级)单例对象 Cache
  • earlySingletonObjects (二级)提前曝光的单例对象 Cache
  • singletonFactories (三级)单例对象工厂 Cache
 /**
* Cache of singleton objects: bean name to bean instance.
* 存放的是单例 bean 的映射。
* <p>
* 对应关系为 bean name --> bean instance
*/
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); /**
* Cache of singleton factories: bean name to ObjectFactory.<br/>
* 存放的是 ObjectFactory,可以理解为创建单例 bean 的 factory 。
* <p>
* 对应关系是 bean name --> ObjectFactory
*/
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); /**
* Cache of early singleton objects: bean name to bean instance.<br/>
* 存放的是早期的 bean,对应关系也是 bean name --> bean instance。
* <p>
* 它与 {@link #singletonFactories} 区别在于 earlySingletonObjects 中存放的 bean 不一定是完整。
* <p>
* 从 {@link #getSingleton(String)} 方法中,我们可以了解,bean 在创建过程中就已经加入到 earlySingletonObjects 中了。
* 所以当在 bean 的创建过程中,就可以通过 getBean() 方法获取。
* <p>
* 这个 Map 也是【循环依赖】的关键所在。
*/
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

这三个缓存集合就是解决Spring中循环依赖的所在,具体流程:

  • 首先从一级缓存singletonObjects中获取,如果为null,且当前bean正在被创建,则从二级缓存earlySingletonObjects中获取,如果还是为null,且允许singletonFactories通过getObject获取,则从三级缓存singletonFactories中获取,如果得到,则将其加入二级缓存earlySingletonObjects中,并从三级缓存singletonFactories中移除对应的工厂对象(因为单例模式的bean只会被创建一次),这样三级缓存就升级到二级缓存了,所以二级缓存存在的意义就是缓存三级缓存中ObjectFactory#getObject的执行结果,提前曝光单例Bean对象。

如果从单例缓存中得到bean对象,则会调用getObjectForBeanInstance方法进一步处理,因为从缓存中得到的bean是最原始的bean,并不一定是最终所需要的bean对象。

上面分析了从缓存中获取bean对象,但是缓存中的值是从什么地方添加进来的呢?如果你调试过源码会发现这样一段代码:

 // AbstractAutowireCapableBeanFactory
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));// 当前单例bean是否正在被创建
if (earlySingletonExposure) {
if (logger.isDebugEnabled()) {
logger.debug("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
// 提前将创建的bean实例加入到singletonFactories中
// 为了后期避免循环依赖
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}

分析:

当一个bean满足三个条件,则会被加入到缓存中。三个条件如下:

  • 单例。
  • 运行提前暴露bean。
  • 当前bean正在被创建中。

DefaultSingletonBeanRegistry#addSingletonFactroy

     protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}

分析:

  • 如果单例缓存中不存在beanName,则将singletonFactory进行缓存[singletonFactories集合],注意这里还有一个earlySingletonObjects.remove操作,该操作是删除二级缓存(提前曝光的单例对象bean),因为二级缓存是从三级缓存转换而来的,因此在对三级缓存[singletonFactories]进行缓存时,需要remove二级缓存。
  • 这段代码发生在createBeanInstance方法之后,也就是说这个bean其实已经被创建出来了,但它还是不是很完美(没有进行属性填充和初始化),但是对于其他依赖它的对象而言已经足够了(可根据对象引用定位到堆中对象),能够被认出来。所以Spring解决setter构造依赖的关键,就是对bean进行提前曝光。

上面介绍的了三级缓存[singletonFactories]与二级缓存[earlySingletonObjects]的出处,接下来看一级缓存的出处:

在DefaultSingletonBeanRegistry#getSingleton函数中:

 public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
// 做同步
synchronized (this.singletonObjects) {
// 从缓存中检查一遍
// 因为singlton模式其实已经复用了创建的bean,所以该步骤必须检查
Object singletonObject = this.singletonObjects.get(beanName);
// 为空,开始进行加载
if (singletonObject == null) {
if (this.singletonsCurrentlyInDestruction) {
throw new BeanCreationNotAllowedException(beanName,
"Singleton bean creation not allowed while singletons of this factory are in destruction " +
"(Do not request a bean from a BeanFactory in a destroy method implementation!)");
}
if (logger.isDebugEnabled()) {
logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
}
// 加载前置处理 其实就是打一个标记
beforeSingletonCreation(beanName);
// 首先将新的newSingleton设置为false
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
// 初始化bean
// 该过程其实是调用createBean()方法 这里是一个回调方法
singletonObject = singletonFactory.getObject();
newSingleton = true;
} catch (IllegalStateException ex) {
// Has the singleton object implicitly appeared in the meantime ->
// if yes, proceed with it since the exception indicates that state.
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
throw ex;
}
} catch (BeanCreationException ex) {
if (recordSuppressedExceptions) {
for (Exception suppressedException : this.suppressedExceptions) {
ex.addRelatedCause(suppressedException);
}
}
throw ex;
} finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
// 一堆异常处理后,进行后置处理 移除标志
afterSingletonCreation(beanName);
}
// 新的bean 加入缓存中
if (newSingleton) {
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}

分析:

这里关注第54行处:addSingleton函数。

 // DefaultSingletonBeanRegistry
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}

分析:

这里就是将bean加入一级缓存中[singletonObjects],同时remove二级缓存和三级缓存中值,因为bean已经被创建成功了,二级缓存与三级缓存也就不需要了。

总结

从上面分析过程可以知道为什么Spring只解决单例模式下的循环依赖了吧?

  • Spring只对单例模式的bean进行了提前暴光[singletonFactories],这也是由于其他scope域bean的特性所致。

单例模式下循环依赖解决流程:

  • 首先A完成初始化第一步并将自己提前曝光出来(通过 ObjectFactory 将自己提前曝光),A在初始化的时候,发现自己依赖对象 B,此时就会去尝试 get(B),这个时候发现 B 还没有被创建出来。
  • 然后B就走创建流程,在B初始化的时候,同样发现自己依赖 C,C也没有被创建出来。
  • 这个时候C又开始初始化进程,但是在初始化的过程中发现自己依赖 A,于是尝试 get(A),这个时候由于A已经添加至缓存中(一般都是添加至三级缓存singletonFactories),通过 ObjectFactory 提前曝光,所以可以通过ObjectFactory#getObject()方法来拿到 A 对象,C 拿到 A 对象后顺利完成初始化,然后将自己添加到一级缓存中
  • 回到B,B 也可以拿到 C 对象,完成初始化,A可以顺利拿到B完成初始化。到这里整个链路就已经完成了初始化过程了。

如果你看过《Spring源码深度解析》这本书,还能发现如下流程图:

注:看图说话,笔者最为喜欢!其实去亲身调试一下循环依赖的代码,可能会有更加深刻的认识。


by Shawn Chen,2019.04.29,下午。

【spring源码分析】IOC容器初始化(十二)的更多相关文章

  1. SPRING源码分析:IOC容器

    在Spring中,最基本的IOC容器接口是BeanFactory - 这个接口为具体的IOC容器的实现作了最基本的功能规定 - 不管怎么着,作为IOC容器,这些接口你必须要满足应用程序的最基本要求: ...

  2. Spring源码解析-ioc容器的设计

    Spring源码解析-ioc容器的设计 1 IoC容器系列的设计:BeanFactory和ApplicatioContext 在Spring容器中,主要分为两个主要的容器系列,一个是实现BeanFac ...

  3. spring源码分析---IOC(1)

    我们都知道spring有2个最重要的概念,IOC(控制反转)和AOP(依赖注入).今天我就分享一下spring源码的IOC. IOC的定义:直观的来说,就是由spring来负责控制对象的生命周期和对象 ...

  4. spring 源码之 ioc 容器的初始化和注入简图

    IoC最核心就是两个过程:IoC容器初始化和IoC依赖注入,下面通过简单的图示来表述其中的关键过程:

  5. Spring源码阅读-IoC容器解析

    目录 Spring IoC容器 ApplicationContext设计解析 BeanFactory ListableBeanFactory HierarchicalBeanFactory Messa ...

  6. Spring源码:IOC原理解析(二)

    版权声明:本文为博主原创文章,转载请注明出处,欢迎交流学习! 接着上一章节的内容,我们来分析当new一个FileSystemXmlApplicationContext对象的时候,spring到底做了那 ...

  7. Spring 源码剖析IOC容器(一)概览

    目录 一.容器概述 二.核心类源码解读 三.模拟容器获取Bean ======================= 一.容器概述 spring IOC控制反转,又称为DI依赖注入:大体是先初始化bean ...

  8. Spring源码解析-IOC容器的实现

    1.IOC容器是什么? IOC(Inversion of Control)控制反转:本来是由应用程序管理的对象之间的依赖关系,现在交给了容器管理,这就叫控制反转,即交给了IOC容器,Spring的IO ...

  9. Spring源码解析-IOC容器的实现-ApplicationContext

    上面我们已经知道了IOC的建立的基本步骤了,我们就可以用编码的方式和IOC容器进行建立过程了.其实Spring已经为我们提供了很多实现,想必上面的简单扩展,如XMLBeanFacroty等.我们一般是 ...

  10. Spring源码之IOC容器创建、BeanDefinition加载和注册和IOC容器依赖注入

    总结 在SpringApplication#createApplicationContext()执行时创建IOC容器,默认DefaultListableBeanFactory 在AbstractApp ...

随机推荐

  1. 如何使用SignTool签署应用程序包

    备注 有关签署UWP应用程序包的信息,请参阅使用SignTool签署应用程序包. 了解如何使用SignTool对Windows应用商店应用包进行签名,以便部署它们.SignTool是Windows软件 ...

  2. Linux 使用 top 命令查看系统的运行情况

    top命令显示界面 top命令默认在一个特定间隔(3秒)后刷新显示.要手动刷新,用户可以输入回车或者空格. top - 18:32:45 up 230 days, 2:01, 1 user, load ...

  3. PHP全栈学习笔记17

    phpmyadmin教程 管理页进入phpmyadmin 打开C:\wamp\apps\phpmyadmin3.5.1下的配置文件:config.inc 修改密码 创建与修改数据库.数据表 字段类型 ...

  4. 超级简单!把文档转换成网页格式(Core)

    因为需要把好多的文档放在服务器上访问,最开始是使用第3方网站www.aconvert.com,这个其实也挺方便的, 最后由于一些需求原因,最终只有依靠代码来进行,以下是简化后的代码 创建一个控制台应用 ...

  5. [Abp 源码分析]零、文章目录

    0.系列文章目录 一.Abp 框架启动流程分析 二.模块系统 三.依赖注入 四.模块配置 五.系统设置 六.工作单元的实现 七.仓储与 Entity Framework Core 八.缓存管理 九.事 ...

  6. 找不到servlet对应的class

    javax.servlet.ServletException: Wrapper cannot find servlet class com.suntomor.lewan.pay.NotifyRecei ...

  7. 【Python3爬虫】教你怎么利用免费代理搭建代理池

    一.写在前面 有时候你的爬虫刚开始的时候可以正常运行,能够正常的爬取数据,但是过了一会,却出现了一个“403 Forbidden",或者是”您的IP访问频率太高“这样的提示,这就意味着你的I ...

  8. 学习ASP.NET Core Razor 编程系列十五——文件上传功能(三)

    学习ASP.NET Core Razor 编程系列目录 学习ASP.NET Core Razor 编程系列一 学习ASP.NET Core Razor 编程系列二——添加一个实体 学习ASP.NET ...

  9. Android SDK 开发——发布使用踩坑之路

    前言 在 Android 开发过程中,有些功能是通用的,或者是多个业务方都需要使用的. 为了统一功能逻辑及避免重复开发,因此将该功能开发成一个 SDK 是相当有必要的. 背景 刚好最近自己遇到了类似需 ...

  10. PyTorch入门(一)向量

    什么是PyTorch?   PyTorch是Facebook人工智能团队开发的一个机器学习和深度学习工具,用于处理大规模图像分析,包括物体检测,分割与分类.但是它的功能不仅限于此.它与其它深度学习框架 ...