@

循环依赖

是什么?

​ 简单的来说就是对象a的属性中引用了对象b,对象b的属性中引用了对象c......最后引用到a。

<bean id="a" class="com.zmm.test.A" lazy-init="false">
<property name="b" ref="b"/>
</bean> <bean id="b" class="com.zmm.test.B" lazy-init="false">
<property name="c" ref="c"/>
</bean> <bean id="c" class="com.zmm.test.C" lazy-init="false">
<property name="a" ref="a"/>
</bean>

Spring是如何解决的?

解决方法

  1. 三级缓存(其实二级缓存也能解决,只是看是否使用AOP)
// DefaultSingletonBeanRegistry类下的

/**
* 一级缓存
* 用来存放成品bean对象(已经成功实例化与初始化了的)
*
* Cache of singleton objects: bean name to bean instance.
*/
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); /**
* 三级缓存 (AOP的关键,如果不用AOP,二级缓存也能解决循环依赖)
* 用来存放早期的bean实例(lambda表达式,一个匿名内部类的形式),看bean对象是否需要被代理。
* ObjectFactory<?>是一个函数式接口,
*
* Cache of singleton factories: bean name to ObjectFactory.
*/
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); /**
* 二级缓存
* 用来存放半成品bean对象,已经实例化了的但是未初始化的bean对象
*
* Cache of early singleton objects: bean name to bean instance.
*/
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
  1. 提前暴露

AbstractAutowireCapableBeanFactory类的doCreateBean()方法

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
// ....... // Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
// 急于缓存单例,以便即使在诸如BeanFactoryAware之类的生命周期接口触发时也能够解析循环引用。 // 是否是单例的 && 允许循环引用 && 是Singleton当前bean正在创作中
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
// 一个匿名内部类,提前暴露创建的实例bean。可以防止循环引用,尽早的持有对象的引用
// 如果一级缓存中不存在指定的bean,就添加到三级缓存中去,将二级缓存中的移除
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
} // ........................ // 如果是提前暴露的单例
if (earlySingletonExposure) {
// 获取指定名称的已注册的单例模式的Bean对象
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
// 如果获取到的bean对象不为空
if (exposedObject == bean) {
// 如果获取到的Bean对象与当前实例化的Bean对象相同
// 将实例暴露出去,当前实例初始化完成
exposedObject = earlySingletonReference;
}
// 当前Bean依赖其他Bean,并且当发生循环引用时不允许创建新的实例对象
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
// 获取当前Bean所依赖的其他Bean
for (String dependentBean : dependentBeans) {
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
// .......
}
}
} // ...... return exposedObject;
}

BeanDefinitionValueResolver类下的resolveValueIfNecessary()方法

// 解析属性值,对注入类型进行转换
public Object resolveValueIfNecessary(Object argName, @Nullable Object value) {
// 对引用类型的属性进行解析
if (value instanceof RuntimeBeanReference) {
RuntimeBeanReference ref = (RuntimeBeanReference) value;
// 调用引用类型的解析方法
return resolveReference(argName, ref);
} // ......对其他类型的属性的解析
}

大致流程

源码分析

​ A引用B、B引用C、C引用A。根据上面大致流程来。

​ 在docreate()方法中,先对a实例化



将a实例的引用暴露出去

准备去填充属性,进入populateBean()方法,applyPropertyValues()方法继续进入,重点来了,resolveValueIfNecessary()方法

继续进入,我们会看到会调用resolveReference()这个方法

最终又会调用getBean()方法

接下来我就省略对b的实例化,直接去看对c的。

继续跟着上面的走,在BeanDefinitionValueResolver类的resolveReference()方法时,调用getBean()去获取了a的实例

从缓存中获取a的实例

我们继续跟进到c实例的doGetBean()方法

这时候,c的实例化初始化已经完成了,然后就是b的初始化以及a的初始化了,步骤类似,自行去debug吧。

细节

1、在三级缓存中,实例的变更情况。例如a、b、c。序号代表顺序

一级缓存:7.添加c、9.添加b、11.添加a

二级缓存:4.添加a、10.移除a

三级缓存:1.添加a、2.添加b、3.添加c、5.移除a、6.移除c、8.移除b

2、关于使用构造器注入和set注入,下面是官网的解释

【Spring】循环依赖的更多相关文章

  1. spring循环依赖问题分析

    新搞了一个单点登录的项目,用的cas,要把源码的cas-webapp改造成适合我们业务场景的项目,于是新加了一些spring的配置文件. 但是在项目启动时报错了,错误日志如下: 一月 , :: 下午 ...

  2. Spring 循环依赖

    循环依赖就是循环引用,就是两个或多个Bean相互之间的持有对方,比如CircleA引用CircleB,CircleB引用CircleC,CircleC引用CircleA,则它们最终反映为一个环.此处不 ...

  3. Springboot源码分析之Spring循环依赖揭秘

    摘要: 若你是一个有经验的程序员,那你在开发中必然碰到过这种现象:事务不生效.或许刚说到这,有的小伙伴就会大惊失色了.Spring不是解决了循环依赖问题吗,它是怎么又会发生循环依赖的呢?,接下来就让我 ...

  4. Spring 循环依赖的三种方式(三级缓存解决Set循环依赖问题)

    本篇文章解决以下问题: [1] . Spring循环依赖指的是什么? [2] . Spring能解决哪种情况的循环依赖?不能解决哪种情况? [3] . Spring能解决的循环依赖原理(三级缓存) 一 ...

  5. Spring循环依赖的解决

    ## Spring循环依赖的解决 ### 什么是循环依赖 循环依赖,是依赖关系形成了一个圆环.比如:A对象有一个属性B,那么这时候我们称之为A依赖B,如果这时候B对象里面有一个属性A.那么这时候A和B ...

  6. 这个 Spring 循环依赖的坑,90% 以上的人都不知道

    1. 前言 这两天工作遇到了一个挺有意思的Spring循环依赖的问题,但是这个和以往遇到的循环依赖问题都不太一样,隐藏的相当隐蔽,网络上也很少看到有其他人遇到类似的问题.这里权且称他非典型Spring ...

  7. Spring — 循环依赖

    读完这篇文章你将会收获到 Spring 循环依赖可以分为哪两种 Spring 如何解决 setter 循环依赖 Spring 为何是三级缓存 , 二级不行 ? Spring 为啥不能解决构造器循环依赖 ...

  8. 帮助你更好的理解Spring循环依赖

    网上关于Spring循环依赖的博客太多了,有很多都分析的很深入,写的很用心,甚至还画了时序图.流程图帮助读者理解,我看了后,感觉自己是懂了,但是闭上眼睛,总觉得还没有完全理解,总觉得还有一两个坎过不去 ...

  9. spring 循环依赖的一次 理解

    前言: 在看spring 循环依赖的问题中,知道原理,网上一堆的资料有讲原理. 但今天在看代码过程中,又产生了疑问. 疑问点如下: // 疑问点: 先进行 dependon 判断String[] de ...

  10. 3.1 spring5源码系列--循环依赖 之 手写代码模拟spring循环依赖

    本次博客的目标 1. 手写spring循环依赖的整个过程 2. spring怎么解决循环依赖 3. 为什么要二级缓存和三级缓存 4. spring有没有解决构造函数的循环依赖 5. spring有没有 ...

随机推荐

  1. 工作之余第二篇(看源码自己实现ArrayList和LinkList)

    先看源码: 首先看构造器,构造器有三种,一种直接给定初始长度的,如下代码 public ArrayList(int initialCapacity) { if (initialCapacity > ...

  2. 微信小程序:Navigator导航组件

    导航组件:类似超链接标签. url:要跳转的页面路径,可以放绝对路径,也可以放相对路径,绝对路径指从pages作为根目录开始找到你要的页面. 找到你要找的页面的相对地址的方法:在vscode中,该页面 ...

  3. python进阶(11)生成器

    生成器 利用迭代器,我们可以在每次迭代获取数据(通过next()方法)时按照特定的规律进行生成.但是我们在实现一个迭代器时,关于当前迭代到的状态需要我们自己记录,进而才能根据当前状态生成下一个数据. ...

  4. 后端程序员之路 5、.conf、libconfig

    .conf在linux里随处可见,作用基本跟windows的.ini差不多 libconfighttp://www.hyperrealm.com/libconfig/libconfig_manual. ...

  5. CentOS7 安装 MySQL Cluster 7.6.7

    引用自:http://lemonlone.com/posts/mysql-ndb-cluster-install/ 仅做备份和配置文件更改 1.先在VMware中安装 CentOS-7-x86_64- ...

  6. CodeBlocks的安装配置以及使用教程

    CodeBlocks的安装配置以及使用教程 教程写的很啰嗦,本来几句话就能搞定的,但为了照顾到那部分真正的小白还请大家见谅! 一.下载 前往CodeBlocks官网下载带编译器的版本,目前的最新版本为 ...

  7. Hi3559AV100 NNIE RFCN开发:V4L2->VDEC->VPSS->NNIE->VGS->VO系统整体动态调试实现

    下面随笔将给出Hi3559AV100 NNIE RFCN开发:V4L2->VDEC->VPSS->NNIE->VGS->VO系统整体动态调试实现,最终的效果是:USB摄像 ...

  8. SpringCloud-服务与注册

    SpringCloud- Eureka服务注册与发现 1.概述 springcloud是一个非常优秀的微服务框架,要管理众多的服务,就需要对这些服务进行治理,管理每个服务与每个服务之间的依赖关系,可以 ...

  9. 『笔记』2-SAT

    前置 \(SAT\) 是适定性( \(Satisfiability\) )问题的简称.一般形式为 \(k \ -\) 适定性问题,简称 \(k-SAT\) .而当 \(k>2\) 时该问题为 \ ...

  10. mongoDB导出-导入数据

    --导出数据集 C:\MongoDB\db\bin>mongoexport -d ttx-xwms-test -c things -o d:\mongo_data\things.txt C:\M ...