# Spring源码 - 从缓存中获取单例Bean

Spring版本:Spring 5.3.13-release


# 1、从缓存中获取单例Bean

单实例BeanSpring的同一个容器中只会创建一次,后续再获取Bean实例就直接从缓存中获取。Spring在获取Bean实例时首先会尝试从缓存中获取。然后再尝试从earlySingletonObjects中加载。因为在创建单实例Bean的时候会存在依赖注入的情况,即当创建A时,发现此时A中需要BSpring此时会先去创建B,然后再将B注入到A中,正因为如此,所以存在循环依赖的情况。Spring为了避免单实例Bean的循环依赖,在创建单实例Bean会将其对象工厂ObjectFactory提前曝光并将其加入Spring中的三级缓存singletonFactories中。一旦下一个Bean创建时需要依赖该Bean,则直接从三级缓存中获取该BeanObjectFactory

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对象。

说明:

singletonObjectsSpring中的一级缓存,用于保存BeanName对应的完全初始化的Bean实例。

earlySingletonObjectsSpring中的二级缓存,用于保存BeanName对应的通过对象工厂实例化的Bean实例。

singletonFactoriesSpring中的三级缓存,用于保存BeanName对应的对象工厂ObjectFactory