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. 实现select下拉框的无限加载(懒加载)

    在实际开发中我们有时无法避免select下拉功能数据过大导致页面卡顿(如在我在一次迭代中有一个select项接口返回了5000多条数据).用户体验差!结合实际开发给出了3个解决方案: 方案1.sele ...

  2. java安全编码指南之:异常处理

    目录 简介 异常简介 不要忽略checked exceptions 不要在异常中暴露敏感信息 在处理捕获的异常时,需要恢复对象的初始状态 不要手动完成finally block 不要捕获NullPoi ...

  3. spring ioc 源码分析之-- beanDefinition的加载过程以及ComponentScan,@componet,@import @Bean等注解解析过程

    背景:我们启动主启动类后,相应的bean就被扫描进来了,原理是啥? 实现该功能的主要核心类就是:ConfigurationClassPostProcessor,我们看看他的继承体系: 它实现了Bean ...

  4. Python-一切皆对象

    Python 动态.灵活根本是什么? Python中一切皆对象,面向对象更加彻底,函数.类也是对象,属于一等公民 一等公民特性 1. 可以赋值给一个变量 def name(name="北门吹 ...

  5. Android作业0930

    1.使用ListView和Adapter实现购物商城 Android 布局文件 <?xml version="1.0" encoding="utf-8"? ...

  6. AA.Dapper升级了

    AA.Dapper基于dapper进一步封装而成的orm框架,提供增删改查.分页.事务.原生sql的功能,以满足日常的业务开发. 1.Repository层: DapperRepository类包含大 ...

  7. Spring的BeanFactory是什么?

    什么是BeanFactory? 提到Spring,总是让人第一时间想起IOC容器,而IOC容器的顶层核心接口就是我们的BeanFactory,如果能够理解BeanFactory的体系结构想必能让我们对 ...

  8. H264裸码流I/P/B帧类型判别

    花了两天时间做了个h264裸流nal类型和frame类型检测的工具,已上传至github,有需要的自行下载. 1.NAL类型检测 nal类型检测非常容易,对照下表即可容易判断类型. 较常用nal类型包 ...

  9. 网站搭建-云服务器是什么-云服务器ECS是什么

    学习上瘾了,本博客关闭,后期再总结整理.

  10. 网络IO模型-异步选择模型(Delphi版)

    其实关于这个模型,网络上也有一个案例说明 老陈使用了微软公司的新式信箱.这种信箱非常先进,一旦信箱里有新的信件,盖茨就会给老陈打电话:喂,大爷,你有新的信件了!从此,老陈再也不必频繁上下楼检查信箱了, ...