# Spring
源码 - 从缓存中获取单例Bean
Spring
版本:Spring 5.3.13-release
# 1、从缓存中获取单例Bean
单实例Bean
在Spring
的同一个容器中只会创建一次,后续再获取Bean
实例就直接从缓存中获取。Spring
在获取Bean
实例时首先会尝试从缓存中获取。然后再尝试从earlySingletonObjects
中加载。因为在创建单实例Bean
的时候会存在依赖注入的情况,即当创建A
时,发现此时A
中需要B
,Spring
此时会先去创建B
,然后再将B
注入到A
中,正因为如此,所以存在循环依赖的情况。Spring
为了避免单实例Bean
的循环依赖,在创建单实例Bean
会将其对象工厂ObjectFactory
提前曝光并将其加入Spring
中的三级缓存singletonFactories
中。一旦下一个Bean
创建时需要依赖该Bean
,则直接从三级缓存中获取该Bean
的ObjectFactory
。
Spring
获取单例Bean
:
DefaultSingletonBeanRegistry#getSingleton()
:
public Object getSingleton(String beanName) {
// 尝试从 Spring 三级缓存中获取单实例 Bean, 允许创建早期对象引用
return getSingleton(beanName, true);
}
# 2、重载方法getSingleton(String beanName, boolean allowEarlyBeanRefrence)
实际调用的是重载方法,DefaultSingletonBeanRegistry#getSingleton(String beanName, boolean allowEarlyBeanRefrence)
:
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 尝试在一级缓存 singletonObjects 中获取
Object singletonObject = this.singletonObjects.get(beanName);
// 如果没有从一级缓存中获取到, 并且获取的 bean 正在创建中
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 从二级缓存 earlySingletonObjects 中获取
singletonObject = this.earlySingletonObjects.get(beanName);
// 如果没有从二级缓存中获取到, 并且允许创建早期对象的引用
if (singletonObject == null && allowEarlyReference) {
// 进入同步代码块, 锁定全局变量并进行处理
synchronized (this.singletonObjects) {
// 再次从 singletonObjects 一级缓存中获取
singletonObject = this.singletonObjects.get(beanName);
// 如果没有从一级缓存中获取到
if (singletonObject == null) {
// 再次从 earlySingletonObjects 二级缓存中获取
singletonObject = this.earlySingletonObjects.get(beanName);
// 如果没有获取到
if (singletonObject == null) {
// 从三级缓存中获取, 三级缓存中获取到的不是我们需要的实例, 三级缓存中保存的是 ObjectFactory 是一个工厂
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
// 如果从三级缓存中获取到
if (singletonFactory != null) {
// 从对象工厂中获取 bean 实例对象(调用工厂方法获取 bean 实例对象, lambda 表达式回调获取)
// 当某些方法需要提前初始化的时候则会调用 addSingletonFactory 方法将对应的 ObjectFactory 初始化策略存储在 singletonFactories 中
singletonObject = singletonFactory.getObject();
// 放入二级缓存中, 一级缓存用于存放完整实例化的 bean 实例, 此时获取的 bean 实例并非完整的 bean 实例
this.earlySingletonObjects.put(beanName, singletonObject);
// 将获取到的对象工厂从三级缓存中移除
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
// 返回从 Spring 三级缓存中获取到的单实例 bean 对象
return singletonObject;
}
这个方法涉及到循环依赖的检查以及Spring
中记录Bean
状态的变量。大致流程如下:
Spring
首先会尝试从一级缓存singletonObjects
中获取,为什么要先从一级缓存中获取。因为Spring
中单实例Bean
是只创建一次的。如果此时从sinletonObjects
中获取到了,证明该Bean
实例Spring
已经创建过,不需要再次进行创建,直接返回即可。- 如果未从
singletonObjects
中获取到,则至少证明该Bean
还未实例化或者还未初始化完全。那么Spring
会继续尝试从二级缓存earlySingletonObjects
中获取,如果从earlySingletonObjects
中获取到了,则直接返回。注意:此时返回的Bean
实例并未完全初始化。 - 如果未从
earlySingletonObjects
中获取到,则至少证明该Bean
还未进行初始化。那么Spring
会继续尝试从三级缓存singletonFactories
中获取。需要注意的是Spring
的三级缓存singletonFactories
中缓存的是beanName
对应的ObjectFactory
对象工厂,并不是我们需要的Bean
实例对象。如果从singletonFactories
中获取到Bean
的对象工厂,那么Spring
会调用该对象工厂的getObject()
方法获取Bean
对象,并将获取到的Bean
对象缓存到Spring
的二级缓存earlySingletonObjects
中,同时将该Bean
的对象工厂从三级缓存singletonFactories
移除并返回获取的Bean
对象。
说明:
singletonObjects
:Spring
中的一级缓存,用于保存BeanName
对应的完全初始化的Bean
实例。
earlySingletonObjects
:Spring
中的二级缓存,用于保存BeanName
对应的通过对象工厂实例化的Bean
实例。
singletonFactories
:Spring
中的三级缓存,用于保存BeanName
对应的对象工厂ObjectFactory
。
GitHub源码地址:https://github.com/kapbc/kapcb-spring-source/tree/master/Spring-Framework-v5.3.13
备注:此文为笔者学习
Spring
源码的笔记,鉴于本人技术有限,文中难免出现一些错误,感谢大家批评指正。