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. 如何使用MathType输入贝塔符号?

    在文档中使用公式,常常需要输入各种符号.比如要输入三角函数公式,那么就要输入三角函数中的阿尔法α.贝塔β等符号,那么要怎么打出这类符号呢? MathType作为强大的数学公式编辑器,可以用来编辑各种数 ...

  2. C# redis集群批量操作之slot计算出16384个字符串

    引入一个大家都用的到的需求来说吧. 需求:要在三主三从的redis集群,存入数据,会对数据进行批量删除操作,数据要求要在redis集群负载均衡. 思路: 1.存入数据好办 1 var connect ...

  3. java中String类的使用

    一.Strng类的概念 String类在我们开发中经常使用,在jdk1.8版本之前(包括1.8),String类的底层是一个char类型的数组,1.8版本之后是byte类型的数组,正是因为String ...

  4. jmeter接口测试多数据组合登陆场景

    一.安装好Java运行环境 百度下载JDK并且配置JAVA环境的教程一搜一大把,这里我就不详说了 二.运行JMETER 打开安装目录的bin文件中的jmeter.bat文件 三.添加程序 1.添加线程 ...

  5. C#设计模式-桥接模式(Bridge Pattern)

    引言 例如我有好几个项目,需要外包出去做各种类型的测试,不同的公司基础费用不同,不同的测试类型价格也是不同的.此时不同的项目选择不同的公司和不同类型的测试进行测试价格都是不同的.于是我们可以创建一个项 ...

  6. C/C++面试题:C++与C有什么不同?

    昨天,小编在一个讨论群里看到这样的对话     有人想要入学校编程俱乐部,面试时,学长问了她C++的区别,她没有答上来,就没有通过. 说到C和C++的区别,不只是进入学校社团有考核,出了学校,找工作面 ...

  7. 创建topic

    sh kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic ...

  8. django中的积累

    只要继承了model.Model, 就会生成一个新的表,但是,如果在Meta方法中添加abstract=True,就不会产生新的表,而是作为一个基类存放多个表共同拥有的方法和字段等 from djan ...

  9. Beta冲刺随笔——Day_Seven

    这个作业属于哪个课程 软件工程 (福州大学至诚学院 - 计算机工程系) 这个作业要求在哪里 Beta 冲刺 这个作业的目标 团队进行Beta冲刺 作业正文 正文 其他参考文献 无 今日事今日毕 林涛: ...

  10. 【常见踩坑】】USB调试安装失败(Installation failed with message INSTALL_CANCELED_BY_USER)

    [参考]http://www.cnblogs.com/liushilin/p/6553918.html 问题:在USB安装调试(小米手机),出现如下错误 解决:1.小米手机解决办法见参考.登录小米账号 ...