栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Spring循环依赖问题和三级缓存

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Spring循环依赖问题和三级缓存

笔记大纲
  • 1.循环依赖的产生
  • 2.Bean的生命周期总览
  • 3.三级缓存
    • 3.1 循环依赖的解决流程
    • 3.2 singletonFactories存在的必要?
    • 3.3 原型Bean的循环依赖
    • 3.4 构造注入的循环依赖问题
  • 4.@Async遭遇循环依赖的启动报错问题

1.循环依赖的产生
public class A {
    @Autowired
    private B b;
}

public class B {
    @Autowired
    private A A;
}

Spring默认是支持循环依赖的,不过也可以通过容器设置关闭。Spring的循环依赖是基于三级缓存的三个Map来完成的,三个Map各自有各自的作用。

2.Bean的生命周期总览
  1. 包扫描、BeanDefinition的生成。
  2. 类加载、BeanDefinition准备生成对应的Bean。
  3. 通过构造推断,选定构造方法,实例化出一个Bean。
  4. 填充属性、依赖注入。
  5. 初始化
  6. 初始化后,判断该Bean是否需要AOP,如果需要则AOP。
  7. 将最终生成的Bean放入单例池singletonObjects这个Map中。
3.三级缓存

传统的循环依赖,不依靠外力干扰肯定是不能实现的,需要一些Map进行实例化过程中的外力干扰。Spring提出了三级缓存的概念,其实就是3个Map:

  - `singletonObjects`:单例池,保存着完整实例化后的Bean,可以直接通过getBean( )获取。
  - `earlySingletonObjects`:残缺单例池,保存着还没实例化完成的Bean(没有注入完成的当前								Bean或者是提前AOP的Bean)。
  - `singletonFactories`:单例工厂,保存的是一个Lamda表达式,表达式的程序执行结果就是要							放入二级缓存的对象。
3.1 循环依赖的解决流程

A在实例化过程中,会提前将创建Bean的一段程序代码:Lamda表达式暴露出来,这个Lamda表达式有可能用不上,也有可能用得上。将这个Lamda表达式缓存在三级缓存singletonFactories这个Map中。
如果发生了循环依赖,B在获取A的时候,先去一级缓存拿,拿不到就去二级缓存拿,如果还是拿不到,则去三级缓存拿(三级缓存一定能拿到),三级缓存拿到的是一个Lamda表达式程序,然后调用这个程序(如果A需要AOP,则生成代理,否则返回A原始对象),拿到以后放入二级缓存且删掉三级缓存,注入给B的A属性。
实例化完成后将完整的Bean,放入单例池。

3.2 singletonFactories存在的必要?

因为SpringBean的声明周期中存在AOP阶段,而AOP产生的对象和原始对象是不同的对象。如果循环依赖没有第三级的缓存,那么在二级缓存里面就是原始对象,而不是最终的AOP代理对象。
当然Spring可以在实例化后,注入之前提前进行AOP的操作,但是这个流程不符合Spring的设计。

3.3 原型Bean的循环依赖

Spring对原型Bean不支持循环依赖,如果发生了会启动报错。因为原型Bean每次都会创建新的对象,包括对象里的属性都是新创建。如果发生了循环依赖,则陷入死循环无法处理而报错。

3.4 构造注入的循环依赖问题

构造注入仍然不支持循环依赖,会报错。和原型Bean的创建类似,通过构造创建A对象,那么B对象必须作为入参,如果B对象都找不到那么构造都无法完成,无法进行下去。

4.@Async遭遇循环依赖的启动报错问题

场景:A依赖B,B依赖A,A中方法加有@Async注解
如果@Async注解+循环依赖会造成启动报错,底层根本原因是给B的A属性注入之后,会检查这个A属性和singltonObjects单例池里A对象是否是同一个,如果不是就会抛这个异常。而@Async注解为什么会改变这个对象,是因为@Async注解是基于beanPostProcessor的,A初始化完成后会基于AOP产生新的对象。而@Transactional不会造成这个问题,是因为事务注解不是基于beanPostProcessor而是基于advisor。
解决这个问题可以使用@Lazy来解决、或者让Spring对B先初始化然后初始化A,就不会有这个问题。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1013422.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号