关于spring的三级缓存
首先大概说一下spring的加载bean的概念(注意,这里是大概,其中应该还包括加载配置文件和封装成beanDefinition等等的一些操作,这些请仔细查看其它的文献)
spring严格按照如下顺序:
- 实例化
- 设置属性
- 初始化(包括执行init方法和静态加载的一些东西)
- 如果该bean需要代理则生成aop代理对象
什么是三级缓存
是spirng中维护的三个map:
/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
缓存 | 说明 |
---|---|
singletonObjects | 第一级缓存,存放可用的成品Bean。 |
earlySingletonObjects | 第二级缓存,存放半成品的Bean,半成品的Bean是已创建对象,但是未注入属性和初始化。用以解决循环依赖。 |
singletonFactories | 第三级缓存,存的是Bean工厂对象,用来生成半成品的Bean并放入到二级缓存中。用以解决循环依赖。 |
循环依赖
所谓的循环依赖,就是两个或则两个以上的bean互相依赖对方,最终形成闭环。比如“A对象依赖B对象,而B对象也依赖A对象”,或者“A对象依赖B对象,B对象依赖C对象,C对象依赖A对象”;类似以下代码:
public class A {
private B b;
}
public class B {
private A a;
}
为什么要有三级缓存
为了解决在aop代理条件下的循环依赖
加载流程如下:
- 调用getBean,缓存中没有则去加载实例化A
- 将A封装成objectFactory放入到三级缓存,此时该对象是半成品,还没有加载完
- 设置A的属性
- 发现A需要B的依赖,然后去实例化B, 并且将B加入到三级缓存
- 设置B的属性,在B中发现需要A的依赖,则调用getBean
- 这次在三级缓存中获取到了A,注意三级缓存中存放的是封装成了objectFactory的对象,获取的具体方法是调用了objectFactory的getObject(该方法的作用是,如果Bean是需要代理的对象则创建一个代理对象并返回,如果不是代理对象则直接返回该对象本身), 并且放入到了二级缓存
- B拿到了A这个半成品的代理引用
- B完成了初始化
- A完成初始化
注意在第三级缓存的作用完全就是从第三级缓存中的objectFactory调用getObject 获取的Bean 放入到二级缓存中 (objectFactory.getObject 方法作用参考上面的第六步, 获取的Bean 可以是代理对象也可以是代理本身)
为什么不是二级缓存
因为如果只有二级缓存,完全可以解决循环依赖,但是如果考虑到aop,情况就完全不一样,如果是在aop的情况下,那么就只能在A实例化完 (注意只是实例化阶段)之后生成代理对象放入到二级缓存, 因为spring在实例化阶段是不会知道这个是否有循环依赖(只有在设置bean属性的时候知道),这个时候二级缓存直接就是代理对象,虽然可以解决循环依赖,但是完全是违背了spring的设计原则
spring的设计原则是初始化阶段完毕后再给Bean生成代理对象
这样子在没有循环依赖的时候,spring在实例化阶段就给生成了代理对象, 而不是在 spirng设计原则中 初始化完毕后生成代理对象
而且如果是在有三级缓存并且是循环依赖情况下,代理对象还能延迟加载, 就是在获取三级缓存调用getObject的时候创建代理对象
参考文献
https://segmentfault.com/a/1190000023647227