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对象创建进行分析 :
- creatingSet< AService >, 标记AService的bean对象为创建中, 放入creatingSet集合中
- 实例化AService对象, 通过new AService()的方式先创建出来AService的普通对象, 这时时候就把new出来的这个AService普通对象放到SingletonFactories(第三级缓存)中, 里面的存放形式是这样的 : SingletonFactories< beanName, lambda表达式(beanName, AService普通对象)>
- 这时候就要给
AService
中的bService
属性 赋值了, 先去单例池(也就是singletonObjects一级缓存)里面找单例的BService, 发现并没有找到, 那就创建bService的Bean对象- 跟最开始创建AService的方式一样, 通过new BService()的方式先创建出来BService的普通对象
- 然后给BService中的 aService属性 赋值, 也是先去单例池(也就是singletonObjects一级缓存)里面找单例的AService,
- 发现并没有找到, 这时候就开始去creatingSet集合查找是否AService对象正在进行创建, 就会发现AService, BService正在进行循环依赖, 然后就从SingletonFactories(第三级缓存)中获取AService的beanName, 这时候就执行lambda表达式(beanName, AService普通对象), lambda表达式判断如果需要提前Aop则生成AService的代理对象(如果不需要提前Aop则生成AService的普通对象),这时候就把这个代理对象(普通对象)赋给BService中的 aService属性.完美, 这样BService中就成功注入aService属性.
- 然后再为bService对象的其他属性赋值, 如果bService也要进行aop, 那么步骤和上面的相同.
- 这样一个成品的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 用了第三级缓存,既维持了设计原则,又处理了循环依赖;牺牲那么一丢丢内存空间是愿意接受的