Spring bean生命周期

可以简化为以下5步。

1、构建BeanDefinition

2、实例化 Instantiation

3、属性赋值 Populate

4、初始化 Initialization(BeanPostprocessor -> Aware,init)

5、销毁 Destruction

Spring 三级缓存作用

一级缓存

/** Cache of singleton objects: bean name to bean instance. */
Map<String, Object> singletonObjects;

用来保存实例化、初始化都完成的bean对象。

二级缓存

/** Cache of early singleton objects: bean name to bean instance. */
Map<String, Object> earlySingletonObjects ;

用来保存实例化完成,但是未初始化完成的对象(这个对象不一定是原始对象,也有可能是经过AOP生成的代理对象)。

三级缓存

/** Cache of singleton factories: bean name to ObjectFactory. */
Map<String, ObjectFactory<?>> singletonFactories;

用来保存一个对象工厂(ObjectFactory),提供一个匿名内部类,用于创建二级缓存中的对象。

三级缓存中提到的ObjectFactory即 () -> getEarlyBeanReference(beanName,mbd,bean),其中bean就是原始对象。

其中getEarlyBeanReference 方法是 SmartInstantiationAwareBeanPostProcessor接口中定义的,AbstractAutoProxyCreator(Spring AOP proxy creator)实现了该方法。

Spring三级缓存实现

获取beanName:A

org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean(beanName:A)
  org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(beanName:A)
    org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(beanName:A, allowEarlyReference:true)

分别按照一级缓存、二级缓存、三级缓存顺序加载。如果存在循环依赖(比如beanName:B依赖beanName:A),而且三级缓存中存在beanName:A的引用,则从三级缓存中拿到beanName:A对应的提早曝光的对象(可能是原始对象,也可能是代理对象)并放入二级缓存。比如又有beanName:C依赖beanName:A,会直接从二级缓存中获取到。

bean创建和初始化完成

org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean(beanName:A)
  org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(beanName:A, ObjectFactory:lamda表达,调用AbstractBeanFactory#createBean(beanName:A))
  org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingleton(beanName:A,singletonObject:A)

直接添加到一级缓存

bean创建完成之后

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean(beanName:A, RootBeanDefinition:mbd, Object[]:args)
  org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean(beanName:A, RootBeanDefinition:mbd, Object[]:args)
    org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingletonFactory(beanName:A, () -> getEarlyBeanReference(beanName:A, mbd, bean:A))
    //放入三级缓存,beanName:A -> ObjectFactory( () -> getEarlyBeanReference(beanName:A, mbd, bean:A) )
  org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean  //属性填充
  org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean   //初始化

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
// Instantiate the bean.
BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = instanceWrapper.getWrappedInstance();
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// Initialize the bean instance.
Object exposedObject = bean;
populateBean(beanName, mbd, instanceWrapper);
exposedObject = initializeBean(beanName, exposedObject, mbd); if (earlySingletonExposure) {
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
}
}
}
}

org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(beanName:A, allowEarlyReference:false)

一个简单的A、B互相依赖循环依赖场景

@Async注解循环依赖报错

@Transactional使用的是自动代理创建器AbstractAutoProxyCreator,它实现了getEarlyBeanReference()方法从而很好的对循环依赖提供了支持。

@Async的代理创建使用的是AsyncAnnotationBeanPostProcessor单独的后置处理器实现的,它只在一处postProcessAfterInitialization()实现了对代理对象的创建,因此若出现它被循环依赖了,就会报BeanCurrentlyInCreationException。

protected Object doCreateBean( ... ){
...
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
... Object exposedObject = bean; /**
*假如A实例有方法注有@Async注解,A实例依赖B实例,B实例依赖A实例。创建A实例时先走到populateBean方法,然后开始填充属性B实例,B实例也会走到populateBean方法,然后从三级缓存中通过A流程中的getEarlyBeanReference()方法,从而拿到A的早期引用*。执行A的getEarlyBeanReference()方法的时候,会执行自动代理创建器,这里最终得到是可能是原始A对象,也可能是代理后的A对象(注意哦,A实例的@Async注解这里还没有被处理呢)。exposedObject此时指向的是原始A实例。
*/
populateBean(beanName, mbd, instanceWrapper); /**
*标注有@Async的A实例的代理对象在此处会被生成, 参照类:AsyncAnnotationBeanPostProcessor。执行完之后,exposedObject指向的是个代理对象而非原始A实例了。
*/
exposedObject = initializeBean(beanName, exposedObject, mbd); ...
// 这里是报错的重点。
if (earlySingletonExposure) {
/**
*因为A被B循环依赖进去了,所以此时A是被放进了二级缓存的,所以此处earlySingletonReference指向的是通过创建A实例流程中的getEarlyBeanReference()返回的A实例(再强调用一下,可能是原始对象,也可能是代理对象)。
*说到这里,什么情况下earlySingletonReference==null?也就是getSingleton(beanName:A, false)==null,只有当A实例没有牵涉到循环依赖的时候(即不存在A依赖B且B依赖A的场景;单独存在B依赖A是没有问题,A的三级缓存根本不会执行,所以二级缓存就不会有值,A创建并初始化完成之后直接放到了一级缓存)。
*/
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
//这里exposedObject指向的是被@Aysnc代理过的对象,而bean是原始对象,所以此处不相等,走else逻辑。
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
/**
*allowRawInjectionDespiteWrapping 标注是否允许此Bean的原始类型被注入到其它Bean里面,即使自己最终会被包装(代理)。
*默认是false表示不允许,如果改为true表示允许,就不会报错啦。这是我们后面讲的决方案的其中一个方案。
*另外dependentBeanMap是记录着每个Bean它所依赖的Bean的Map。
*/
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
//因为A依赖于B,所以此处拿到了B实例的beanName
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length); /**
*B实例经过removeSingletonIfCreatedForTypeCheckOnly最终返返回false 因为alreadyCreated里面已经有它了表示B已经完全创建完成了。
*既然B实例已经创建完成了,通过创建A实例流程中的getEarlyBeanReference()返回的A实例已经注入到了B实例中,此时B实例注入的和exposedObject指向的不是同一个A实例,那肯定就有问题了。
*/
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.");
}
}
}
}
...
}

具体原因分析参考:https://blog.csdn.net/f641385712/article/details/92797058

总结

1、Spring 解决循环依赖有两个前提条件:不全是构造器方式的循环依赖,必须是单例。

2、如果没有出现循环依赖,第三级缓存(singletonFactories)将不会使用到,对象会按照Spring创建bean的生命周期流程,最后将bean直接放到第一级缓存(singletonObjects)中。

3、一定要三级缓存嘛,二级缓存不能解决循环依赖?不能,主要是为了生成代理对象。

因为三级缓存中放的是生成具体对象的匿名内部类(ObjectFactory),它可能生成代理对象,也可能是普通的实例对象。使用三级缓存主要是为了保证不管什么时候使用的都是一个对象。假设只有二级缓存的情况,往二级缓存中放的显示一个普通的Bean对象,BeanPostProcessor去生成代理对象之后,覆盖掉二级缓存中的普通Bean对象,无法保证程多线程环境下获取到bean对象一致性。

彻底理解Spring如何解决循环依赖的更多相关文章

  1. 一张图彻底理解Spring如何解决循环依赖!!

    写在前面 最近,在看Spring源码,看到Spring解决循环依赖问题的源码时,不得不说,源码写的太烂了.像Spring这种顶级的项目源码,竟然存在着这种xxx的代码.看了几次都有点头大,相信很多小伙 ...

  2. Spring 如何解决循环依赖问题?

    在关于Spring的面试中,我们经常会被问到一个问题,就是Spring是如何解决循环依赖的问题的. 这个问题算是关于Spring的一个高频面试题,因为如果不刻意研读,相信即使读过源码,面试者也不一定能 ...

  3. Spring如何解决循环依赖问题

    目录 1. 什么是循环依赖? 2. 怎么检测是否存在循环依赖 3. Spring怎么解决循环依赖 本文主要是分析Spring bean的循环依赖,以及Spring的解决方式. 通过这种解决方式,我们可 ...

  4. Spring 如何解决循环依赖的问题

    Spring 如何解决循环依赖的问题 https://blog.csdn.net/qq_36381855/article/details/79752689 Spring IOC 容器源码分析 - 循环 ...

  5. Spring如何解决循环依赖

    一.什么是循环依赖 多个bean之间相互依赖,形成了一个闭环. 比如:A依赖于B.B依赖于c.c依赖于A 通常来说,如果问spring容器内部如何解决循环依赖, 一定是指默认的单例Bean中,属性互相 ...

  6. Spring如何解决循环依赖?

    介绍 先说一下什么是循环依赖,Spring在初始化A的时候需要注入B,而初始化B的时候需要注入A,在Spring启动后这2个Bean都要被初始化完成 Spring的循环依赖有两种场景 构造器的循环依赖 ...

  7. 【Spring】 Spring如何解决循环依赖的问题?

    https://mp.weixin.qq.com/s/FtbzTMxHgzL0G1R2pSlh-A 通常来说,如果问Spring内部如何解决循环依赖,一定是单默认的单例Bean中,属性互相引用的场景. ...

  8. Spring中解决循环依赖报错的问题

    什么是循环依赖 当一个ClassA依赖于ClassB,然后ClassB又反过来依赖ClassA,这就形成了一个循环依赖: ClassA -> ClassB -> ClassA 原创声明 本 ...

  9. Spring如何解决循环依赖,你真的懂了?

    导读 前几天发表的文章SpringBoot多数据源动态切换和SpringBoot整合多数据源的巨坑中,提到了一个坑就是动态数据源添加@Primary接口就会造成循环依赖异常,如下图: 这个就是典型的构 ...

随机推荐

  1. 如何使用Camtasia进行电脑录制屏幕

    在现在的网络互联网时代,越来越多的人走上了自媒体的道路.有些自媒体人会自己在网络上录制精彩视频,也有一些人会将精彩.热门的电影剪辑出来再加上自己给它的配音,做成大家喜欢看的电影剪辑片段.相信不管大家是 ...

  2. MySQL优化篇(未完待续)

    一.优化SQL语句的一般步骤 1.通过 show status命令了解各种sql的执行频率 mysql客户端连接成功后,通过show[session|global] status命令,可以查看服务器的 ...

  3. Hibernate框架session的方法

    package Test; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernat ...

  4. Arduion学习(一)点亮三色发光二极管

    这是我接触Arduion以来第一个小实验 实验准备: 1.查阅相关资料,了解本次实验所用到的引脚.接口的相关知识. 2.准备Arduion板(本次实验所用到的型号为mega2560).三色发光二极管. ...

  5. Python实现树莓派摄像头持续录像并传送到主机

    关于树莓派,想必从事嵌入式开发的开发者都有听过,树莓派原名为Raspberry Pi,也就是它的英文读法,树莓派诞生于英国,由"Raspberry Pi 基金会"这个慈善组织注册开 ...

  6. Access数据库简介

    一.Access数据库的简介 1.microsoft office access是由微软发布的关联式数据库管理系统.它结合了 microsoft jet database engine 和 图形用户界 ...

  7. gitee模拟备份

    这个作业的要求在哪里 作业要求 团队名称 欢天喜地七仙女 团队成员 王玮晗.林鑫宇. 黄龙骏.陈少龙.何一山.崔亚明.陆桂莺 这个作业的目标 按照要求进行编码,按要求新建码云仓库.上传文件.拍摄视频 ...

  8. 【NOIP2017提高A组模拟9.7】JZOJ 计数题

    [NOIP2017提高A组模拟9.7]JZOJ 计数题 题目 Description Input Output Sample Input 5 2 2 3 4 5 Sample Output 8 6 D ...

  9. 20200513_安装windows sql server 2012 _ ws功能 NetFx3时出错,错误代码:-2146498298

    这是没有安装.net Framework 3.5造成的 1. 下载个.net Framework 3.5, 放到任意目录下, 比如C: 2. 打开添加windows 功能 3. 直接下一步: 4. 勾 ...

  10. PyQt(Python+Qt)学习随笔:QDockWidget停靠部件的dockWidgetArea和docked属性

    专栏:Python基础教程目录 专栏:使用PyQt开发图形界面Python应用 专栏:PyQt入门学习 老猿Python博文目录 dockWidgetArea和docked属性这两个属性在Design ...