终于懂了spring 三级缓存和循环依赖

Java开发,总会遇到问三级缓存的。

看了很多文章,感觉不是很透彻。然后昨天下午看阅了spring的源码, 又结合以下的案例对spring的三级缓存和循环依赖问题进行了总结

前言

先看这张图, AService中有BService对象, Bservice中有AService对象, A的创建需要依赖B, B的创建又依赖于A. 到底是先创建A还是先创建B

其实这个问题就像一个经典的哲学问题, 是先有只因还是先有蛋.

在这里插入图片描述

三级缓存, 三个map

首先我们得知道 三级缓存里面分别存的什么

一级缓存里存的是成品对象,实例化和初始化都完成了,我们的应用中使用的对象就是一级缓存中的

二级缓存中存的是半成品,用来解决对象创建过程中的循环依赖问题

三级缓存中存的是 ObjectFactory<?> 类型的 lambda 表达式,用于处理存在 AOP 时的循环依赖问题
在这里插入图片描述

一级缓存

在一级缓存中所有单例的bean初始化完成后会存放在一个Map(singletonObjects)中,beanName为key,单例bean为value。(一级缓存中存放的都是成品Bean对象)

在这里插入图片描述

下面我们就对AService的Bean对象创建进行分析 :

  1. creatingSet< AService >, 标记AService的bean对象为创建中, 放入creatingSet集合中
  2. 实例化AService对象, 通过new AService()的方式先创建出来AService的普通对象, 这时时候就把new出来的这个AService普通对象放到SingletonFactories(第三级缓存)中, 里面的存放形式是这样的 : SingletonFactories< beanName, lambda表达式(beanName, AService普通对象)>
  3. 这时候就要给AService中的 bService属性 赋值了, 先去单例池(也就是singletonObjects一级缓存)里面找单例的BService, 发现并没有找到, 那就创建bService的Bean对象
    1. 跟最开始创建AService的方式一样, 通过new BService()的方式先创建出来BService的普通对象
    2. 然后给BService中的 aService属性 赋值, 也是先去单例池(也就是singletonObjects一级缓存)里面找单例的AService,
    3. 发现并没有找到, 这时候就开始去creatingSet集合查找是否AService对象正在进行创建, 就会发现AService, BService正在进行循环依赖, 然后就从SingletonFactories(第三级缓存)中获取AService的beanName, 这时候就执行lambda表达式(beanName, AService普通对象), lambda表达式判断如果需要提前Aop则生成AService的代理对象(如果不需要提前Aop则生成AService的普通对象),这时候就把这个代理对象(普通对象)赋给BService中的 aService属性.完美, 这样BService中就成功注入aService属性.
    4. 然后再为bService对象的其他属性赋值, 如果bService也要进行aop, 那么步骤和上面的相同.
    5. 这样一个成品的bService就会被放入单例池Map(singletonObjects), 也就是我们的一级缓存中

三级缓存

三级缓存singletonFactories存放ObjectFactory<?> 类型的 lambda 表达式

用于处理存在 AOP 时的循环依赖问题,传入的是匿名内部类,ObjectFactory.getObject() 方法最终会调用getEarlyBeanReference()进行处理,返回创建bean实例化的lambda表达式。

在这里插入图片描述

二级缓存

既然觉得前面的一三级缓存就能解读循环依赖问题, 那么二级缓存是干什么的?
在这里插入图片描述
有这么一种情况:

在BService里具有aService属性, CService里也有aService属性

这时候bService对象从SingletonFactories(第三级缓存)中获取AService的beanName, 这时候就执行lambda表达式, 生成了一个aService对象, cService对象也从SingletonFactories(第三级缓存)中获取AService的beanName, 总不能再执行lambda表达式, 又生成了一个新的aService对象吧? 两个aService对象? 这样就不符合spring的单例的设计模式了, 所以呢这时候就要用到二级缓存了.

不管是bService还是cService谁先生成aService, 把先生成的aService对象放在二级缓存(earlySingletonObjects)中,后生成的那个发现二级缓存中有aService就不会再去创建. 当下次又需要使用到aService的时候, 就先从二级缓存中获取, 找不到再从一级缓存中找.

在这里插入图片描述
如此看来二级缓存和三级缓存都是用来打破循环依赖的, 二级缓存中放的是半成品的对象: 要么是lambda表达式进行提前aop的生成的sService代理对象, 要么是不进行aop生成的sService普通对象, 虽然此时的对象也就被创建了出来, 但是呢还都没有被spring赋值, 所以叫做半成品对象

总结

1、三级缓存各自的作用

第一级缓存存的是对外暴露的对象,也就是我们应用需要用到的

第二级缓存的作用是为了处理循环依赖的对象创建问题,里面存的是半成品对象或半成品对象的代理对象

第三级缓存的作用处理存在 AOP + 循环依赖的对象创建问题,能将代理对象提前创建

2、Spring 为什么要引入第三级缓存

严格来讲,第三级缓存并非缺它不可,因为可以提前创建代理对象

提前创建代理对象只是会节省那么一丢丢内存空间,并不会带来性能上的提升,但是会破环 Spring 的设计原则

Spring 的设计原则是尽可能保证普通对象创建完成之后,再生成其 AOP 代理(尽可能延迟代理对象的生成)

所以 Spring 用了第三级缓存,既维持了设计原则,又处理了循环依赖;牺牲那么一丢丢内存空间是愿意接受的
在这里插入图片描述

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿千弟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值