# 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源码的笔记,鉴于本人技术有限,文中难免出现一些错误,感谢大家批评指正。