spring在单例,非构造方法注入的情况下允许循环依赖

1.循环依赖

a引用b,b引用a。a创建的时候需要b,但是b没有创建,需要先去创建b,b创建的时候又没有a,这就出现的循环依赖的问题

2.为什么单例,setter注入才能解决?

(1)构造器注入是在实例化对象时反射调用构造器去注入参数,所以既然beanA、beanB的都拿不到完整的依赖,就会进行无限的循环调用。setter注入方式 setter注入方式就是new出一个对象后,调用该对象的set方法对属性进行赋值。此时对象已经被new出来了,只不过是不完整而已。 如果出现了循环依赖的问题,这就要比构造器注入的方式好的多 所以Spring对于循环依赖问题的解决就是针对于setter方法的

(2)多例的bean是不需要放入到IOC容器中的

3.三级缓存

spring解决循环依赖主要是通过三级缓存

        // 用于存储完整的bean,接下来称之为【一级缓存】
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); // 用于存储不完整的bean,完成实例化,但是还未进行属性注入及初始化的对象,接下来称之为【二级缓存】
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16); //提前暴露的一个单例工厂,二级缓存中存储的就是从这个工厂中获取到的对象,接下来称之为【三级缓存】
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

4.基本流程

(1)当创建A对象的时候,会调用getBeanSington()方法,也就是去单例池(一级缓存)中查找有没有A的Bean,A还没创建成功,肯定没有。

(2)调用下一个getBeanSington()方法,判断A对象是否正在被创建,也就是去查找set集合中有没有A对象,这时候也没有,然后将A加入set集合中

(3)判断是否支持循环依赖,是否是单例,是否允许提前暴露。是的话将A实例(反射空参构造。只实例化,没有属性注入)放入singltonFactors三级缓存,其实这儿存的是一个工厂,后面就是通过这个工厂获取对象的

(4)A开始注入属性B

(5)流程和前面一样,这样的话三级缓存里就有了A和B

(8)B也开始注入属性A

(9)去单例池获取A的Bean,没有获取到

(10)去set集合中获取A对象,获取到了,说明A正在被创建

(11)依次去二级缓存,三级缓存获取A

(12)在三级缓存获取到A对象工厂,调用工厂的方法获取到A对象(如果A对象做了代理,就会在这儿进行代理),A对象放到二级缓存

(13)B对象注入属性A完成(此时A对象还没有完成属性注入,B只是持有A的一个引用),B进入单例池

(14)A对象此时就能从单例池中获取到B对象

(15)A对象完成初始化

         protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException { // 默认调用无参构造实例化Bean
// 构造方法的依赖注入,就是发生在这一步
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
// 实例化后的Bean对象,这里获取到的是一个原始对象,即没有进行属性填充的对象
final Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass(); //...... // 解决循环依赖的关键步骤
// earlySingletonExposure:是否”提前暴露“原始对象的引用
// 因为不论这个bean是否完整,他前后的引用都是一样的,所以提前暴露的引用到后来也指向完整的bean
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
// 如果需要提前暴露单例bean,则将该bean工厂放入【三级缓存】中
if (earlySingletonExposure) {
// 将刚创建的bean工厂放入三级缓存中singleFactories(key是beanName,value是FactoryBean)
// 同样也会移除【二级缓存】中对应的bean,即便没有
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
} // Initialize the bean instance.
Object exposedObject = bean;
try {
//填充属性(依赖注入)
populateBean(beanName, mbd, instanceWrapper);
//调用初始化方法,完成bean的初始化操作
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
//...... return exposedObject;
}

5.为什么要使用三级缓存呢?二级缓存能解决循环依赖吗?

如果要使用二级缓存解决循环依赖,意味着所有Bean在实例化后就要完成AOP代理,这样违背了Spring设计的原则,Spring在设计之初就是通过AnnotationAwareAspectJAutoProxyCreator这个后置处理器来在Bean生命周期的最后一步来完成AOP代理,而不是在实例化后就立马进行AOP代理。

【源码】spring循环引用的更多相关文章

  1. Spring 循环引用(二)源码分析

    Spring 循环引用(二)源码分析 Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.html) Spring 循环引用相关文章: & ...

  2. Spring 循环引用(三)源码深入分析版

    @ 目录 前言 正文 分析 doGetBean 为什么Prototype不可以 createBean doCreateBean getEarlyBeanReference getSingleton b ...

  3. Spring IOC 容器源码分析 - 循环依赖的解决办法

    1. 简介 本文,我们来看一下 Spring 是如何解决循环依赖问题的.在本篇文章中,我会首先向大家介绍一下什么是循环依赖.然后,进入源码分析阶段.为了更好的说明 Spring 解决循环依赖的办法,我 ...

  4. Dubbo 源码分析 - 服务引用

    1. 简介 在上一篇文章中,我详细的分析了服务导出的原理.本篇文章我们趁热打铁,继续分析服务引用的原理.在 Dubbo 中,我们可以通过两种方式引用远程服务.第一种是使用服务直联的方式引用服务,第二种 ...

  5. Spring 循环引用(一)一个循环依赖引发的 BUG

    Spring 循环引用(一)一个循环依赖引发的 BUG Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.html) Spring 循环 ...

  6. 3.2spring源码系列----循环依赖源码分析

    首先,我们在3.1 spring5源码系列--循环依赖 之 手写代码模拟spring循环依赖 中手写了循环依赖的实现. 这个实现就是模拟的spring的循环依赖. 目的是为了更容易理解spring源码 ...

  7. 5.2 Spring5源码--Spring AOP源码分析二

    目标: 1. 什么是AOP, 什么是AspectJ 2. 什么是Spring AOP 3. Spring AOP注解版实现原理 4. Spring AOP切面原理解析 一. 认识AOP及其使用 详见博 ...

  8. 5.2 spring5源码--spring AOP源码分析二--切面的配置方式

    目标: 1. 什么是AOP, 什么是AspectJ 2. 什么是Spring AOP 3. Spring AOP注解版实现原理 4. Spring AOP切面原理解析 一. 认识AOP及其使用 详见博 ...

  9. 3.4 spring5源码系列--循环依赖的设计思想

    前面已经写了关于三篇循环依赖的文章, 这是一个总结篇 第一篇: 3.1 spring5源码系列--循环依赖 之 手写代码模拟spring循环依赖 第二篇: 3.2spring源码系列----循环依赖源 ...

  10. 52、[源码]-Spring源码总结

    52.[源码]-Spring源码总结 总结 一.Spring容器在启动的时候,先会保存所有注册进来的Bean的定义信息: xml注册bean: 注解注册Bean:@Service.@Component ...

随机推荐

  1. springboot maven项目运行常见报错 及ajax请求报错

    如图所示 tomcat运行后直接停止,也不报错 原因:我的原因是controller路径配置重名或者service没有配置@Service 遇见这错找了好久问题,网上也搜不到,特此记录一下 问题2 a ...

  2. 记录jmeter使用beanshell断言获取复杂的json字符串参数值

    实战示例 测试场景 电商系统经常会涉及到商品的库存数量的压测,在用户下单前需要先做库存余量的判断,当余量不足时用户无法下单,保证商品的有效售卖 库存余量查询响应结果 响应结果一般是json字符串的形式 ...

  3. Django-发送注册、忘记密码邮件验证-send_mail

    用户邮箱注册.发送验证码流程图 那,如何解决? 1.  setting配置邮箱参数 # 邮箱设置,需要在邮箱中开启smtp服务 # 提供服务的主机域名 EMAIL_HOST = 'smtp.163.c ...

  4. Mice and Rice(queue的用法)

    Mice and Rice(queue的用法) Mice and Rice is the name of a programming contest in which each programmer ...

  5. CF600E Lomsat gelral 树上启发式合并

    题目描述 有一棵 \(n\) 个结点的以 \(1\) 号结点为根的有根树. 每个结点都有一个颜色,颜色是以编号表示的, \(i\) 号结点的颜色编号为 \(c_i\)​. 如果一种颜色在以 \(x\) ...

  6. kail使用sunJDK

    今天在安装软件的时候报错,提示应使用oracle的JDK,所以鼓捣了一会发现个简单的方法 1.下载安装包解压文件到opttar -xzvf jdk-8u91-linux-x64.tar.gz 2.设置 ...

  7. Python下的图像处理库,你选哪个?

    奥里给~ 转载:https://blog.csdn.net/chen801090/article/details/105795068/ 在进行数字图像处理时,我们经常需要对图像进行读取.保存.缩放.裁 ...

  8. Java中的对象都是在堆上分配的吗?

    作者:LittleMagic https://www.jianshu.com/p/8377e09971b8 为了防止歧义,可以换个说法: Java对象实例和数组元素都是在堆上分配内存的吗? 答:不一定 ...

  9. Spring Boot+Spring Security+JWT 实现 RESTful Api 认证(二)

    Spring Boot+Spring Security+JWT 实现 RESTful Api 认证(二) 摘要 上一篇https://javaymw.com/post/59我们已经实现了基本的登录和t ...

  10. Centos 6.9 安装 php5.6

    1.检查当前安装的PHP包 yum list installed | grep php 如果有安装的PHP包,先删除他们, 如: yum remove php.x86_64 php-cli.x86_6 ...