循环依赖指的是在对象之间存在相互依赖关系,形成一个闭环,导致无法准确地完成对象的创建和初始化;当两个或多个对象彼此之间相互引用,而这种相互引用形成一个循环时,就可能出现循环依赖问题。
在早期的 Spring 版本中是可以自动解决的循环依赖的问题的,
public class A {
@Autowired
private B b;
}
public class B {
@Autowired
private A a;
}
但要注意,Spring 解决循环依赖是有前提条件的,
第一,要求互相依赖的 Bean 必须要是单例的 Bean。
第二,依赖注入的方式不能都是构造函数注入的方式。
public class C {
private D d;
@Autowired
public C(D d) {
this.dService = dService;
}
}
public class D {
private C c;
@Autowired
public D(C c) {
this.c = c;
}
}
Spring 源码中关于三级缓存的定义如下:
// 一级缓存
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// 二级缓存
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
// 三级缓存
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
所以说,所谓的“三级缓存”就是是指三个 Map 数据结构,分别用于存储不同状态的 Bean。
一级缓存:
一级缓存保存的是已经完全初始化和实例化的 Bean 对象,在程序中使用的 Bean 通常就是从这个缓存中获取的;这个缓存的目的是确保 Bean 只初始化一次(是单例的),避免多次实例化相同的Bean对象,提高性能。
二级缓存:
二级缓存用来解决 Bean 创建过程中的循环依赖问题,它存储的是尚未完成属性注入和初始化的“半成品”Bean 对象;当 Spring容器发现两个或多个 Bean 之间存在循环依赖时,也就是当一个 Bean 创建过程中需要引用另一个正在创建的 Bean,Spring 将创建需要的这些未完全初始化的对象提前暴露在二级缓存中,以便其他 Bean 进行引用,确保它们之间的依赖能够被满足。
三级缓存:
三级缓存中存储的是 ObjectFactory> 类型的代理工厂对象,主要用于处理存在 AOP 时的循环依赖问题;每个 Bean 都对应一个 ObjectFactory 对象,通过调用该对象的 getObject 方法,可以获取到早期暴露出去的 Bean;在该 Bean 要被其他 Bean 引用时,Spring 就会用工厂对象创建出该 Bean 的实例对象,最终当该 Bean 完成构造的所有步骤后就会将该 Bean 放入到一级缓存中。
当 Spring 发生循环依赖时(以最开始介绍的场景为例,A B 两个 Bean 相互依赖),以下是完善的执行流程:
getObject()
方法获取 A 的属性,此时,A 被存入二级缓存,同时清除三级缓存;因此,B 能够成功注入 A 属性,B 接着执行初始化,处于实例化和初始化都已完成的完全状态。addSingleton()
,将完全状态的 B 存入一级缓存,并清空二级,三级缓存。beanFactory.getBean()
方法获取 B,由于第六步已经将 B 存入一级缓存,A 可以直接获取 B,成功注入 B 属性, A 接着执行初始化,得到一个完全状态的 A。addSingleton()
,将完全状态的 A 存入一级缓存,并清空二级缓存。此时,A 和 B 都被实例化和初始化完成,解决了循环依赖的问题;这个流程确保了每个Bean在需要时都能够获取到已完全初始化的依赖项。
常见疑问解答
问题一:为什么在 Bean B 被注入 Bean A 之前,需要将 Bean A 存入二级缓存?
问题二:为什么创建完 Bean 后要清空二、三级缓存?
问题三:三级缓存为什么不能解决构造器引起的循环依赖?
问题四:如果不使用三级缓存,只使用二级缓存,能否解决循环依赖?
肯定是不能的,二级缓存存储的 Bean 可能是两种类型,一种是实例化阶段创建出来的对象,另一种是实例化阶段创建出来的对象的代理对象;是否需要代理对象取决于你的配置需要,如是否添加了事务注解或自定义 AOP 切面;如果放弃使用三级缓存,即没有 ObjectFactory,那么就需要将早期的 Bean 放入二级缓存;但问题是,应该将未被代理的 Bean 还是代理的 Bean 放入二级缓存,这只能在属性注入阶段,处理注解时才能分辨。
Spring 在一个三级缓存放置一个工厂,如果产生循环依赖 ,这个工厂的作用就是判断这个对象是否需要代理,如果否则直接返回,如果是则返回代理对像。
在项目中双击 Shift,全局查找文件:AbstractAutowireCapableBeanFactory,找到 550 行左右的 doCreateBean 方法,重点看一下 580 行到 600 行这20行代码就行,包含了三级缓存、属性注入、初始化,精华都在这20行,下面在源码中给出了关键注释。
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
// 通过BeanDefinition实例化对象
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
// Allow post-processors to modify the merged bean definition.
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Post-processing of merged bean definition failed", ex);
}
mbd.postProcessed = true;
}
}
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
// 对象是否单例、是否未创建完成
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
// 将对象的工厂加入到三级缓存
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
// 属性注入 (在这里解析@Autowired注解时,触发循环依赖)
populateBean(beanName, mbd, instanceWrapper);
// 初始化
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
throw (BeanCreationException) ex;
}
else {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
}
}
if (earlySingletonExposure) {
// 从缓存中获取 Bean
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
if (!actualDependentBeans.isEmpty()) {
throw new BeanCurrentlyInCreationException(beanName,
"Bean with name '" + beanName + "' has been injected into other beans [" +
StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
"] in its raw version as part of a circular reference, but has eventually been " +
"wrapped. This means that said other beans do not use the final version of the " +
"bean. This is often the result of over-eager type matching - consider using " +
"'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
}
// Register bean as disposable.
try {
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}
return exposedObject;
}
从缓存中获取 Bean 的源码
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// Quick check for existing instance without full singleton lock
Object singletonObject = this.singletonObjects.get(beanName);
// 从一级缓存中获取
// 如果一级缓存里没有,且 Bean 正在创建中
// 就从二级缓存里获取
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
singletonObject = this.earlySingletonObjects.get(beanName);
// 二级缓存没有,就从三级缓存获取一个工厂
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
// Consistent creation of early reference within full sin
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) {
// 能获取到工厂则创建 Bean
singletonObject = singletonFactory.getObject();
// 把实例存入二级缓存
this.earlySingletonObjects.put(beanName, singletonObject);
// 把工厂从三级缓存移除
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
总之,Spring 的三级缓存机制是一个巧妙的设计,它解决了在 Bean 初始化过程中可能出现的循环依赖问题;对于 Spring 的用户来说,这意味着更加稳定和可靠的 Bean 管理和依赖注入机制。