当前位置: 首页 > news >正文

spring如何用三级缓存解决循环依赖问题

spring为何会出现循环依赖问题?

        我们举个会产生循环依赖的例子,如下所示,可以看到AService类中依赖了BService类,同理呢,BService类中依赖了AService类,这就是所谓的循环依赖。

@Component("aService")
public class AService {

    @Autowired
    private BService bService;
}


@Component("bService")
public class BService {

    @Autowired
    private AService aService;
}

          如果我们使用如下方式就不会有问题。

AService a = new AService();

BService b = new BService();

a.bService = b;

b.aService = a;

       那为何spring框架中怎么就会有循环依赖问题?我们需要从spring bean的生命周期去理解,生命周期主要包括如下几个步骤:

1. Bean定义加载阶段

2.实例化阶段

3.属性填充阶段 

       我们在这一步要将BService的实例赋值给AService类中的bService属性,但是BService此时还没有实例化呢,于是BService要进行实例化,但是BService实例化的过程中发现要注入AService实例,于是就又要去实例化AService,两个类走到这一步都还没有完成Bean的初始化呢,也就是这时还不能被拿来使用呢,这样的话,两者就就会互相依赖,永远无法结束了。

4.Aware接口回调阶段

5.初始化前处理阶段

6.初始化阶段

7.初始化后处理阶段

      这个阶段如果我们的类需要进行AOP的话,得到的对象是AOP对象,也就是代理对象,我们都知道AOP是spring的核心特性之一,通过AOP我们可以在不改变原有类代码的情况下对功能进行增强。最终就绪的Bean是要放到单例池中的。

8.Bean就绪阶段

9.销毁阶段

1.1 什么是第一级缓存,为何需要它?

        我们都知道使用@Component注解将类交给spring容器管理,这种是单例模式的,也就是spring默认情况下,相同名称的类只给这个类生成单个实例。那我们如何来实现我们获取的是同一个实例对象呢?这时候就用到了我们的第一级缓存(singletonObjects),也叫做单例池缓存,它是ConcurrentHashMap,之所以用它是因为ConcurrentHashMap是线程安全的,从而确保大家拿到的都是同一个对象。

1.2 什么是第二级缓存,为何需要它?

        上面我们在属性填充阶段AService和BService两个类互相循环依赖,由于两个类都还没有完成初始化,因此陷入死循环了,要想打破死循环,就得想办法增加第二级缓存(earlySingletonObjects),这个缓存我们要缓存的是实例化完但还未初始化的半成品对象,这样上面属性填充阶段,AService实例化之后就将半成品存放到二级缓存当中,我们从上面bean生命周期中第7步可以看到我们的类如果需要进行AOP的话,最终完成初始化的完整bean是要放到单例池中的,而且放到单例池中的对象是AOP对象,显然我们目前无论AService实例化还是BService实例化,都不是代理对象而是真正的对象本身,但是呢我们又不能等到初始化后再放到二级缓存,这时我们便不得不将AOP提前到实例化阶段了,然后填充BService属性的时候,实例化BService,而BService实例化后也将半成品AOP对象存放到二级缓存当中,然后BSerivce实例化后填充属性AService的时候,发现二级缓存中有这个类的半成品AOP对象,于是就从二级缓存中拿到未初始化完成的AService类的AOP对象,这样就解决了循环依赖的问题。

1.3 什么是第三级缓存,为何需要它?

        如果我们的AService和BService不需要进行AOP,那么我们在填充属性阶段到底该向二级缓存中存入半成品AOP对象呢还是半成品实际对象?这时光靠我们目前已知的两级缓存已经不够用了,我们还需要引入第三级缓存(singletonFactories),这一级缓存会将对象的进行AOP所需要的参数组装成一个ObjectFactory,存入第三级缓存时只是将ObjectFactory存放到第三级缓存了,还没有执行里面的进行是否需要AOP判断以及进行AOP的动作。有了这一级缓存的好处就是,当我们先检查第一级和第二级缓存里面都没有要注入的对象时,就可以从第三级缓存中取出类相关的信息,判断是否需要进行AOP,如果需要就提前进行AOP,从第三级缓存中取出ObjectFactory之后,就从第三级缓存中删除了,因为我们提前进行AOP或者不需要进行AOP场景下将实例化完但还未初始化的半成品对象就要放到二级缓存了。第三级缓存就没有用处了,第三级缓存和第二级等于是互斥的。

       上面在初始化之后进行如果需要进行AOP,由于我们前面已经提前进行AOP了,因此直接从二级缓存中拿就行了,到了这一步AOP对象也是完整的了。如果不需要进行AOP,那么这时拿到的也是初始化完整的实际对象,最后将完整AOP对象或实际对象存放到一级缓存单例池中。

        以上便是spring通过三级缓存解决循环依赖的情况。

 

相关文章:

  • 构建成功后前端程序如何不重新构建再次指向后端服务
  • 问题的根源还是解题的方案
  • 八股总结(数据库)实时更新!
  • SpringBoot(三)环境隔离/外部化配置/单元测试/可观测性/生命周期
  • 自然语言处理(21:(第六章1.)基于RNN生成文本)
  • Cocos Creator Shader入门实战(七):RGB不同算法效果的实现,及渲染技术、宏定义、属性参数的延伸配置
  • Linux系统下C语言fork函数使用案例
  • 热门索尼S-Log3电影感氛围旅拍LUTS调色预设 Christian Mate Grab - Sony S-Log3 Cinematic LUTs
  • AI 知识库是什么?企业如何构建智能化知识管理体系?
  • YOLOv8-YOLO12目标检测模型的标签格式和数据结构详细说明
  • 在rockylinux9.4安装mongodb报错:缺少:libcrypto.so.10文件库
  • 前端开发使用若依的优势
  • 计算机四级网络工程师高频考点
  • JAVA学习*简单的代理模式
  • 机器学习的一百个概念(4)下采样
  • Ground Truth(真实标注数据):机器学习中的“真相”基准
  • UE5学习笔记 FPS游戏制作32 主菜单,暂停游戏,显示鼠标指针
  • 【STL】stack
  • Java数据结构-栈和队列
  • Open GL ES ->GLSurfaceView+离屏渲染滤镜作用的Bitmap+动态顺序叠加滤镜作用链的RecyclerView
  • 网站对公司的作用是什么意思/企业网站搜索引擎推广方法
  • 南京企业微信网站建设/网盘搜索引擎
  • c 怎么做网站开发/湖南企业seo优化推荐
  • h5网站架设/优化关键词具体要怎么做
  • 崇州网站制作/seo优化一般包括
  • 佛山网站制作网站/北京竞价托管代运营