在以前的Spring解决循环依赖的文章中,简单提了以下三级缓存以及为什么需要三级缓存。此处结合代码来说明一下。
什么是三级缓存
Spring三级缓存位于:
DefaultSingletonBeanRegistry
类中,是三个Map成员变量。singletonObjects
:这里的bean是已经创建完成的,该bean经历过实例化->属性填充->初始化以及各类的后置处理。因此,一旦需要获取bean时,我们第一时间就会寻找一级缓存earlySingletonObjects
:这里跟一级缓存的区别在于,该缓存所获取到的bean是提前曝光出来的,是还没创建完成的。也就是说获取到的bean只能确保已经进行了实例化,但是属性填充跟初始化肯定还没有做完,因此该bean还没创建完成,仅仅能作为指针提前曝光,被其他bean所引用singletonFactories
:创建中单例Bean的原始工厂。将bean从三级缓存中取出后会调用SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference
方法。并且从三级缓存中删除该Bean,放入二级缓存。
在
DefaultSingletonBeanRegistry#getSingleton
方法中,我们可以看到它从三个地方取值(这里用来锁双重检测,请注意)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// Quick check for existing instance without full singleton lock
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
// Consistent creation of early reference within full singleton lock
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}根据这里取值的顺序,我们简单把
singletonObjects
定义为一级缓存,earlySingletonObjects
定义为二级缓存,singletonFactories
定义为三级缓存。请注意,这个代码里面有一个很重要的点,那就是第一次从三级缓存中取值后,当前beanName会被移出三级缓存,然后放入二级缓存。
代码分析为什么要三级缓存
以下是Spring创建Bean的方法,
AbstractAutowireCapableBeanFactory#doCreateBean
的部分代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41// AbstractAutowireCapableBeanFactory#doCreateBean
/*忽略部分代码*/
// 创建实例包装
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
// 从实例包装后获取Bean
Object bean = instanceWrapper.getWrappedInstance();
/*忽略部分代码*/
if (earlySingletonExposure) {
/*忽略部分代码*/
// 如果允许暴露,则将其加入二级缓存。在调用被取出来的时候会调用SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference方法,默认实现只有AbstractAutoProxyCreator,创建代理。因此,如果在被暴露的过程中,有其他Bean调用getSingleton(当前对象),则当前对象会被代理(如果有的话),然后放入二级缓存。
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
/*忽略部分代码*/
Object exposedObject = bean;
try {
// 填充属性
populateBean(beanName, mbd, instanceWrapper);
// 实例化Bean,请注意这里,实例化Bean的时候可能会导致exposedObject的改变!!!
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
/*忽略部分代码*/
}
if (earlySingletonExposure) {
// 直接从二级缓存或一级缓存中获取Bean,注意,这里如果不为null,只有一种可能,在暴露出去的时间里,在其他Bean的初始化过程中调用了getSingleton(当前Bean)。
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
// 判断exposedObject在经过initializeBean后是否和原来的Bean是一个对象。
if (exposedObject == bean) {
// 如果bean没有在initializeBean改变,那么将当前Bean替换为代理后(如果有代理过的话)的Bean。
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
/*忽略部分代码*/
// 注意:到了这里,说明了一种情况,当前暴露出去的Bean(可能被代理过后)被其他Bean引用了,但是在实例化Bean的时候,Bean和暴露出去的Bean已经不是一个Bean的,即其他Bean引用的Bean是一个错误的Bean。因此如果有其他的Bean实际引用了当前Bean,则应该抛出一个异常。为什么是实际引用呢?因为这里可能其他Bean只是实现了Aware之类的接口,并在里面调用了getSingleton(当前Bean),并没有实际引用。
}
}
}
总结
- 通过上面的代码分析,我们可以得出SP三级缓存中每个缓存的作用:
- 第一级缓存(singletonObjects)是用来缓存创建好的单例Bean的
- 第二级缓存(earlySingletonObjects)是用来发现在当前Bean被暴露的时间里,有没有被其他Bean引用,如果被其他Bean引用,其他Bean又是引用的一个错误版本,就抛出一个异常,防止程序注入错误。
- 第三级缓存(singletonFactories),这个才是SP真正用来解决循环依赖的,并且可以延迟创建代理的时机。