Java传家宝:微信公众号(Java传家宝)、Java传家宝-B站、Java传家宝-知乎、Java传家宝-CSND
Spring 三级缓存
说到Spring的三级缓存,我们可以先思考一个问题。即存在两个对象A和B,对象A依赖对象B,而对象B又依赖对象A,那么此时就出现了循环依赖问题,如果要创建A对象,那么必然需要B对象,反之B对象同理,而Spring是如何创建A对象和B对象的呢?这就是循环依赖问题,如下:
@Component
public class A {
private B b;
}
//==================
@Component
public class B {
private A a;
}
出现这种情况时,就使用到了Spring的三级缓存。先用大白话来讲:
- 当Spring执行到实例化对象A完毕,在属性注入时发现b不存在
- 那么就去创建B对象,此时对象A已经实例化完了,所以属性a注入成功
- B成功创建完成,返回到A对象的属性注入过程,此时b已经存在
- A完成b属性注入,初始化完成,得到完整的A对象
注意:前面在为对象B设置属性a的时候,这个A类型的属性a还是个半成品,即a还没有完成b属性注入。但是需要注意的是,这个a是一个引用,所以在最后a的b属性注入完成后,也就相当于B的属性a半成品变成了成品对象。
说的有点绕,说白了就是B之前注入的是半成品的属性a,等到A对象最终完成创建后,B中的a属性才算成品对象。
理论知识
大白话就说到这,下面进行源码分析之前,先理清各级缓存的名字和作用,这里引用西柚dzh的图片:
- 一级缓存,singletonObjects,concurrentHashMap结构。用于保存完成了实例化、属性注入、初始化以及一系列的后置处理器的调用最终得到的完整的Bean实例对象。
- 二级缓存,earlySingletonObjects,HashMap结构。用于保存完成了实例化,但属性注入未完成的半成品实例对象。
- 三级缓存,singletonFactories,HashMap结构。用于保存Bean的创建工厂,以便后续有机会创建代理对象。
源码分析
我们将A和B作为循环依赖的Bean
前面就不分析了,我们主要看创建Bean A实例这部分的源码,直接进到doGetBean部分,只放了关键部分:
protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException {
// 从缓存中取Bean对象
Object sharedInstance = this.getSingleton(beanName);
// Create bean instance.
if (!typeCheckOnly) {
// 标记对象正在被创建
this.markBeanAsCreated(beanName);
}
if (mbd.isSingleton()) {
// 这里就尝试创建目标对象,第二个参数传的就是一个ObjectFactory类型的对象
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
});
}
return adaptBeanInstance(name, beanInstance, requiredType);
}
可以看到:1、先尝试从缓存中取到对象,如果没取到;2、那么就标记该对象正在被创建;3、然后尝试去创建对象,
首先如何去缓存取得对象:
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 去一级缓存取对象
Object singletonObject = this.singletonObjects.get(beanName);
// 没取到 && 该对象正在创建
if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
// 去二级缓存中取对象
singletonObject = this.earlySingletonObjects.get(beanName);
// 没取到 && 允许早期引用
if (singletonObject == null && allowEarlyReference) {
synchronized(this.singletonObjects) {
// 重新确定一二级缓存中不存在该Bean
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
// 去三级缓存中取对应的ObjectFactory对象
ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// 三级缓存存在,调用getObject方法返回Bean对象或者Bean的代理对象
singletonObject = singletonFactory.getObject();
// 添加到二级缓存中
this.earlySingletonObjects.put(beanName, singletonObject);
// 从一级缓存中移除
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
可以看到,步骤如下:
- 首先去一级缓存singletonObjects中取完整对象,如果没有且当前对象正在被创建中,
- 那么就去二级缓存earlySingletonObjects中取半成品对象,如果还没取到且allowEarlyReference==true,默认传入的就是true,
- 那么就第二次确认一二级缓存中确实不存在,然后就去三级缓存中找到对应的ObjectFactory对象
- 如果存在ObjectFactory对象,那么就调用对应的getObject方法得到Bean对象或者Bean的代理对象,最后返回该对象
- 上述步骤条件有一个不成立,那么直接返回null即可(一般是该对象不在缓存中也不在创建中)
标记对象就不说了,看一下第三步尝试创建对象,如下:
// AbstractBeanFactory
// 传入了一个函数式方法
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
});
// DefaultSingletonBeanRegistry
public Object getSingleton(String beanName, <?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
synchronized(this.singletonObjects) {
// 先去缓存拿
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
//...
try {
// 调用getObject也就是传入的creatBean方法创建Bean或者代理Bean
singletonObject = singletonFactory.getObject();
newSingleton = true;
} //...
} finally {
//...
this.afterSingletonCreation(beanName);
}
if (newSingleton) {
this.addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
我们进入到singletonObject = singletonFactory.getObject()这一步里面,其实就是调用了createBean方法,如下:
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
if (instanceWrapper == null) {
// 创建实例
instanceWrapper = this.createBeanInstance(beanName, mbd, args);
}
//..
// 保存创建Bean的方法到三级缓存中
this.addSingletonFactory(beanName, () -> {
return this.getEarlyBeanReference(beanName, mbd, bean);
});
}
Object exposedObject = bean;
try {
// 属性注入
this.populateBean(beanName, mbd, instanceWrapper);
// 初始化
exposedObject = this.initializeBean(beanName, exposedObject, mbd);
} //...
}
到这我们就实例化了Bean A并将其对应的创建方法保存到了三级缓存中,它会根据Bean A是否被代理返回对应的原始Bean或者代理Bean。
接下来属性注入时如果遇到循环依赖的Bean B未创建,那么就会通过getBean执行上述一样的流程,将其保存到三级缓存中,然后B在进行属性注入时,在通过getSingleton()取缓存中A实例的时候,会发现三级缓存存在A实例,那么调用getObject获取对象A,并将其添加到二级缓存中。然后为B的属性注入A实例,完成属性注入以及后续的初始化动作。然后将完整的B添加在一级缓存中,移除二三级缓存中的B实例。然后跳转回A的属性注入过程,此时B实例已经缓存了完整对象,直接注入,A的属性注入完成,后续初始化结束,得到完整的A实例,将其添加到一级缓存中,然后删除二三级缓存中的A。