转自链接:https://blog.csdn.net/lyc_liyanchao/article/details/83099675
通过前几节的分析,已经成功将bean实例化,但是大家一定要将bean的实例化和完成bean的创建区分开,bean的实例化仅仅是获得了bean的实例,该bean仍在继续创建之中,之后在该bean实例的基础之上,还要做很多额外的操作,例如bean的属性填充、处理器的应用、bean的循环依赖解决等,今天我们就来分析下Spring是如何解决bean之间的循环依赖。

当ClassA引用ClassB,ClassB又引用ClassA,那么两个类之间就会形成一个闭环,导致循环依赖的出现。大家只需记住一点,Spring只能解决单例模式下的Setter循环依赖。

1.测试用例
bean和xml

 package com.lyc.cn.v2.day01.cycle;

 /**
* @author: LiYanChao
* @create: 2018-10-16 23:59
*/
public class ClassA {
private ClassB classB; public ClassB getClassB() {
return classB;
} public void setClassB(ClassB classB) {
this.classB = classB;
}
}
 package com.lyc.cn.v2.day01.cycle;

 /**
* @author: LiYanChao
* @create: 2018-10-16 23:59
*/
public class ClassB {
private ClassA classA; public ClassA getClassA() {
return classA;
} public void setClassA(ClassA classA) {
this.classA = classA;
}
}
<!--循环依赖-->
<bean id="classA" class="com.lyc.cn.v2.day01.cycle.ClassA" scope="singleton">
<property name="classB" ref="classB"></property>
</bean>
<bean id="classB" class="com.lyc.cn.v2.day01.cycle.ClassB" scope="singleton">
<property name="classA" ref="classA"></property>
</bean>

结果

========测试方法开始=======

com.lyc.cn.v2.day01.cycle.ClassB@2d6a9952
com.lyc.cn.v2.day01.cycle.ClassA@22a71081 ========测试方法结束=======

当scope="singleton"时结果是正常的,Spring为我们解决了bean之间的循环依赖,再将scope改为prototype,运行测试用例(摘取部分异常信息):

Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'classA': Requested bean is currently in creation: Is there an unresolvable circular reference?
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:255)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:198)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:372)
... 40 more

从异常信息中可以看到Is there an unresolvable circular reference?,有循环依赖异常,这也证明了Spring是不能解决prototype作用域的bean之间的循环依赖的。

下面我们从源码角度去分析,Spring是如何解决bean之间的循环依赖问题的。

 protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException {

     // Instantiate the bean.
// ① 实例化bean
BeanWrapper instanceWrapper = null;
// 注意factoryBeanInstanceCache是ConcurrentMap,remove方法会返回删除的键值(如果不存在返回null)
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
// 如果factoryBeanInstanceCache没有缓存对应的BeanWrapper,则重新创建bean实例
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
} // Allow post-processors to modify the merged bean definition.
// ② 允许MergedBeanDefinitionPostProcessor后处理器修改已合并的bean定义。
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Post-processing of merged bean definition failed", ex);
}
mbd.postProcessed = true;
}
} // Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
// ③ 提前缓存ObjectFactory以解决bean之间的循环依赖
// mbd.isSingleton()->是否单例,Spring只解决单例bean的循环依赖问题
// allowCircularReferences->是否允许循环依赖
// isSingletonCurrentlyInCreation->该bean是否创建中
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
} // Initialize the bean instance.
// ④ 初始化bean实例 这里大家要与第①步区分开,到这里bean已经完成了实例化,但是还没有完成初始化的操作,例如bean的属性填充
Object exposedObject = bean;
try {
// 填充bean属性
populateBean(beanName, mbd, instanceWrapper);
// 初始化bean
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
throw (BeanCreationException) ex;
}
else {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
}
} // ⑤ 循环依赖检查
if (earlySingletonExposure) {
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
if (!actualDependentBeans.isEmpty()) {
throw new BeanCurrentlyInCreationException(beanName,
"Bean with name '" + beanName + "' has been injected into other beans [" +
StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
"] in its raw version as part of a circular reference, but has eventually been " +
"wrapped. This means that said other beans do not use the final version of the " +
"bean. This is often the result of over-eager type matching - consider using " +
"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
} // Register bean as disposable.
try {
// ⑥ 根据bean的作用域注册bean
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}
// ⑦ 返回bean实例
return exposedObject;
}

通过第一步已经获得了bean的实例(第二步留在以后再讲解),直接看第三步:提前缓存ObjectFactory以解决bean之间的循环依赖。

1.提前曝光对象
这里涉及到一个非常重要的接口ObjectFactory,该接口是一个函数式接口且只有一个方法:T getObject() throws BeansException;,该方法用于返回一个bean的实例,此时的bean已经完成初始化,但是尚未完成创建。

如果当前的bean满足条件,则将当前正在创建的bean和其ObjectFactory对象提前曝光,加入到正在创建bean池中。

 protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}

2.循环依赖的解决
在完成bean的实例创建之后,还要填充bean的属性,针对ClassA,其属性是ClassB,如果要填充ClassA的属性则势必先要实例化ClassB,那么这里又涉及到一个概念,RuntimeBeanReference–>运行时引用。

打开BeanDefinitionValueResolver类的resolveValueIfNecessary方法。摘取代码片段(该方法会在以后全部分析)

判断RuntimeBeanReference属性

 // ① RuntimeBeanReference->运行时引用
// 例如BeanA依赖BeanB,那么在配置文件中有通过配置ref标签进行引用的,在解析BeanDefinition的时候,是不会直接实例化BeanB的,那么这个引用就是RuntimeBeanReference
if (value instanceof RuntimeBeanReference) {
RuntimeBeanReference ref = (RuntimeBeanReference) value;
return resolveReference(argName, ref);
}

解析RuntimeBeanReference(运行时引用)

 private Object resolveReference(Object argName, RuntimeBeanReference ref) {
try {
// 1、解析引用beanName
Object bean;
String refName = ref.getBeanName();
refName = String.valueOf(doEvaluate(refName));
// 2、判断引用bean是否属于父BeanFactory
if (ref.isToParent()) {
if (this.beanFactory.getParentBeanFactory() == null) {
throw new BeanCreationException(
this.beanDefinition.getResourceDescription(), this.beanName,
"Can't resolve reference to bean '" + refName +
"' in parent factory: no parent factory available");
}
bean = this.beanFactory.getParentBeanFactory().getBean(refName);
}
// 3、从当前beanFactory获取引用beanName实例
else {
bean = this.beanFactory.getBean(refName);
this.beanFactory.registerDependentBean(refName, this.beanName);
}
if (bean instanceof NullBean) {
bean = null;
}
return bean;
}
catch (BeansException ex) {
throw new BeanCreationException(
this.beanDefinition.getResourceDescription(), this.beanName,
"Cannot resolve reference to bean '" + ref.getBeanName() + "' while setting " + argName, ex);
}
}

该过程很简单,首先解析refBeanName,然后通过getBean方法获取其实例,此时当前创建的bean是ClassA,引用bean是ClassB。

获取到ClassB实例之后,又要填充ClassB的属性,此时又会出现对RuntimeBeanReference的解析,即ClassA,再去获取ClassA的实例,此时的ClassA的实例已经被提前曝光,会从缓存中获取ClassA的实例。

 protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 1、从缓存中获取bean
Object singletonObject = this.singletonObjects.get(beanName);
// 2、未能获取到bean,但是允许对当前创建的单例的早期引用(解决循环引用)
// isSingletonCurrentlyInCreation-->判断指定的单例bean是否当前正在创建(Spring只解决单例bean的循环依赖问题)
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
// 从earlySingletonObjects获取提前曝光的bean
singletonObject = this.earlySingletonObjects.get(beanName);
// 未能获取到提前曝光的bean且当前的bean允许被创建早期依赖
if (singletonObject == null && allowEarlyReference) {
// 从缓存中获取BeanFactory
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// 通过getObject()方法获取提前曝光的bean
singletonObject = singletonFactory.getObject();
// 将获取到的singletonObject缓存至earlySingletonObjects
this.earlySingletonObjects.put(beanName, singletonObject);
// 从singletonFactories移除bean
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}

从singletonObjects中无法获取到bean的实例,因为此时bean尚未完成全部创建,但是由于我们提前曝光了ObjectFactory,所以通过singletonObject = singletonFactory.getObject();是可以获取到bean的实例的。这样就解决了Spring的循环依赖问题。

3.总结
Spring只能解决Setter方法注入的单例bean之间的循环依赖
ClassA依赖ClassB,ClassB又依赖ClassA,形成依赖闭环。Spring在获取ClassA的实例时,不等ClassA完成创建就将其曝光加入正在创建的bean缓存中。在解析ClassA的属性时,又发现依赖于ClassB,再次去获取ClassB,当解析ClassB的属性时,又发现需要ClassA的属性,但此时的ClassA已经被提前曝光加入了正在创建的bean的缓存中,则无需创建新的的ClassA的实例,直接从缓存中获取即可。从而解决循环依赖问题。
————————————————
原文链接:https://blog.csdn.net/lyc_liyanchao/article/details/83099675

Spring解决bean之间的循环依赖的更多相关文章

  1. Spring里bean之间的循环依赖解决与源码解读

    通过前几节的分析,已经成功将bean实例化,但是大家一定要将bean的实例化和完成bean的创建区分开,bean的实例化仅仅是获得了bean的实例,该bean仍在继续创建之中,之后在该bean实例的基 ...

  2. Spring框架是怎么解决Bean之间的循环依赖的 (转)

    问题: 循环依赖其实就是循环引用,也就是两个或则两个以上的bean互相持有对方,最终形成闭环.比如A依赖于B,B依赖于C,C又依赖于A.如下图:   如何理解“依赖”呢,在Spring中有: 构造器循 ...

  3. Spring源码分析之循环依赖及解决方案

    Spring源码分析之循环依赖及解决方案 往期文章: Spring源码分析之预启动流程 Spring源码分析之BeanFactory体系结构 Spring源码分析之BeanFactoryPostPro ...

  4. Spring源码-IOC部分-循环依赖-用实例证明去掉二级缓存会出现什么问题【7】

    实验环境:spring-framework-5.0.2.jdk8.gradle4.3.1 Spring源码-IOC部分-容器简介[1] Spring源码-IOC部分-容器初始化过程[2] Spring ...

  5. 再探循环依赖 → Spring 是如何判定原型循环依赖和构造方法循环依赖的?

    开心一刻 一天,侄子和我哥聊天,我坐在旁边听着 侄子:爸爸,你爱我妈妈吗? 哥:这话说的,不爱能有你吗? 侄子:确定有我不是因为荷尔蒙吗? 哥:因为什么荷尔蒙,因为爱情! 侄子:那我妈花点钱,你咋老说 ...

  6. Spring IOC原理补充(循环依赖、Bean作用域等)

    文章目录 前言 正文 循环依赖 什么是循环依赖? Spring是如何解决循环依赖的? 作用域实现原理以及如何自定义作用域 作用域实现原理 自定义Scope BeanPostProcessor的执行时机 ...

  7. Spring 事务、异步和循环依赖有什么关系?

    前言 在循环依赖中有一种循环依赖,就是自注入:自己依赖自己. 事务的自注入 在 Spring 自调用事务失效,你是怎么解决的? 有小伙伴提出可以自己注入自己来解决事务失效. 具体使用方式如下: @Sl ...

  8. Spring学习--Bean 之间的关系

    Bean 之间的关系:继承.依赖. Bean 继承: Spring 允许继承 bean 的配置 , 被继承的 bean 称为父 bean , 继承这个父 bean 的 bean 称为子 bean. 子 ...

  9. Spring源码--debug分析循环依赖--构造器注入

    目的:源码调试构造器注入,看看是怎么报错的. spring:5.2.3 jdk:1.8 一.准备 首先准备两个循环依赖的类:userService和roleServic <bean id=&qu ...

随机推荐

  1. MNIST 源码解析

    # Copyright 2015 Google Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 ...

  2. java 开发中 dom4j的简单用法

    Java中处理XML的方式有很多种,个人任务dom4j还是比较好用的.下面介绍以下简单的使用方法 先把import补充上 import org.dom4j.Document; import org.d ...

  3. 【47】迁移学习(Transfer Learning)

    迁移学习(Transfer Learning) 如果你要做一个计算机视觉的应用,相比于从头训练权重,或者说从随机初始化权重开始,如果你下载别人已经训练好网络结构的权重,你通常能够进展的相当快,用这个作 ...

  4. 双向队列 SDUT 1466

    题目描述      想想双向链表……双向队列的定义差不多,也就是说一个队列的队尾同时也是队首:两头都可以做出队,入队的操作.现在给你一系列的操作,请输出最后队列的状态:命令格式:LIN X  X表示一 ...

  5. Spring MVC 定时任务注解说明

    一.注解说明. Spring 自带的定时任务执行@Scheduled注解,可以定时的.周期性的执行一些任务.查看@Scheduled的注解可以看到有以下三种: 1.1 String cron() de ...

  6. Python之filter()函数与替代实现

    介绍 filter(f,x)函数用于过滤序列并返回迭代器,结果保留x中f为True的元素,需要新的序列通过list()转换. 例子 过滤列表中的字符串,保留数字. >>> i = [ ...

  7. QS Network ZOJ - 1586 板子题

    #include<iostream> #include<algorithm> using namespace std; ; struct edge{ int a,b; doub ...

  8. The Way to Home CodeForces - 910A

    4个月前做的一道题,当时不知道为什么,写了一个bfs,直接就超时了. 现在再看这个题目,发现就是一个简单的贪心,每次走最远即可. #include <bits/stdc++.h> usin ...

  9. Canvas如何绘制精美的图?

    一.Canvas的基本使用 首先在使用Canvas一般先在<body>中添加: <canvas id="></canvas> 然后使用Js进行获取canv ...

  10. PCI Express

    1.1课题研究背景 在目前高速发展的计算机平台上,应用软件的开发越来越依赖于硬件平台,尤其是随着大数据.云计算的提出,人们对计算机在各个领域的性能有更高的需求.日常生活中的视频和图像信息包含大量的数据 ...