Spring通过三级缓存解决循环依赖问题的过程详解
作者:韵秋梧桐
1. 三级缓存解决的问题场景
循环依赖指的是在对象之间存在相互依赖关系,形成一个闭环,导致无法准确地完成对象的创建和初始化;当两个或多个对象彼此之间相互引用,而这种相互引用形成一个循环时,就可能出现循环依赖问题。
在早期的 Spring 版本中是可以自动解决的循环依赖的问题的,
public class A { @Autowired private B b; } public class B { @Autowired private A a; }
但要注意,Spring 解决循环依赖是有前提条件的,
第一,要求互相依赖的 Bean 必须要是单例的 Bean。
这是因为对于原型范围的 Bean(prototype scope),每次请求都会创建一个新的 Bean 实例,这样每次尝试解析循环依赖时,都会产生新的 Bean 实例,导致无限循环,由于没有全局的、持续的单例实例的缓存来引用,因此循环依赖无法得到解决。
第二,依赖注入的方式不能都是构造函数注入的方式。
当使用构造函数注入时,一个 Bean 的实例在构造函数被完全调用之前是不会被创建的;如果 Bean A 的构造函数依赖于 Bean B,而 Bean B 的构造函数又依赖于 Bean A,那么就会产生一个死锁的情况,因为两者都不能在对方初始化之前完成初始化。
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。
2. 三级缓存的差异性
一级缓存:
一级缓存保存的是已经完全初始化和实例化的 Bean 对象,在程序中使用的 Bean 通常就是从这个缓存中获取的;这个缓存的目的是确保 Bean 只初始化一次(是单例的),避免多次实例化相同的Bean对象,提高性能。
二级缓存:
二级缓存用来解决 Bean 创建过程中的循环依赖问题,它存储的是尚未完成属性注入和初始化的“半成品”Bean 对象;当 Spring容器发现两个或多个 Bean 之间存在循环依赖时,也就是当一个 Bean 创建过程中需要引用另一个正在创建的 Bean,Spring 将创建需要的这些未完全初始化的对象提前暴露在二级缓存中,以便其他 Bean 进行引用,确保它们之间的依赖能够被满足。
三级缓存:
三级缓存中存储的是 ObjectFactory<?> 类型的代理工厂对象,主要用于处理存在 AOP 时的循环依赖问题;每个 Bean 都对应一个 ObjectFactory 对象,通过调用该对象的 getObject 方法,可以获取到早期暴露出去的 Bean;在该 Bean 要被其他 Bean 引用时,Spring 就会用工厂对象创建出该 Bean 的实例对象,最终当该 Bean 完成构造的所有步骤后就会将该 Bean 放入到一级缓存中。
3. 循环依赖时的处理流程
当 Spring 发生循环依赖时(以最开始介绍的场景为例,A B 两个 Bean 相互依赖),以下是完善的执行流程:
- 遍历待创建的所有 beanName:在容器启动时,Spring 会遍历所有需要创建的 Bean 名称,在第一次遍历到 A 时,就开始获取 Bean A;如果 Bean A 的实例不在一级、二级缓存中(缓存中没有值),Spring 会开始正常的 Bean 创建流程。
- Bean A 的创建:Bean A 的创建过程开始,然后 Spring 会检查是否 A 是单例(Singleton),同时 A 是否已经创建完成;如果 A 是单例且尚未创建完成,将 A 的 BeanFactory 存入三级缓存。
- 处理依赖注入:A 开始处理 @Autowired 注解,尝试注入 B 属性;Spring 会在一级、二级缓存中来查找 Bean B,如果找不到,则开始正常创建 Bean B 的流程。
- Bean B 的创建:Bean B 的创建过程类似于 A,首先判断B是否是单例,且是否已创建完成,如果否,B 的BeanFactory 也会被存入三级缓存。
- B 注入 A 属性:B 开始注入 A 属性,尝试从一级、二级缓存中查找 A;如果在缓存中找不到 A,B 会尝试从三级缓存获取 A 的 BeanFactory,并通过 BeanFactory的getObject()方法获取 A 的属性,此时,A 被存入二级缓存,同时清除三级缓存;因此,B 能够成功注入 A 属性,B 接着执行初始化,处于实例化和初始化都已完成的完全状态。
- B 存入一级缓存:B执行addSingleton(),将完全状态的 B 存入一级缓存,并清空二级,三级缓存。
- A 继续注入 B 属性:A 继续注入 B 属性,调用beanFactory.getBean()方法获取 B,由于第六步已经将 B 存入一级缓存,A 可以直接获取 B,成功注入 B 属性, A 接着执行初始化,得到一个完全状态的 A。
- A 存入一级缓存:A 执行addSingleton(),将完全状态的 A 存入一级缓存,并清空二级缓存。
此时,A 和 B 都被实例化和初始化完成,解决了循环依赖的问题;这个流程确保了每个Bean在需要时都能够获取到已完全初始化的依赖项。
常见疑问解答
问题一:为什么在 Bean B 被注入 Bean A 之前,需要将 Bean A 存入二级缓存?
主要原因是,如果存在其他循环依赖需要用到 A,从二级缓存中直接取出早期的 Bean A 对象会更加高效。
问题二:为什么创建完 Bean 后要清空二、三级缓存?
清空是为了节省存储空间,一旦 Bean 完全初始化并存储在一级缓存中,其在二、三级缓存中的记录就不再需要了。
问题三:三级缓存为什么不能解决构造器引起的循环依赖?
这是因为构造器引起的循环依赖发生在 Bean 的实例化阶段,这个阶段比二、三级缓存处理的阶段还要早,无法创建出早期的半成品对象。
问题四:如果不使用三级缓存,只使用二级缓存,能否解决循环依赖?
肯定是不能的,二级缓存存储的 Bean 可能是两种类型,一种是实例化阶段创建出来的对象,另一种是实例化阶段创建出来的对象的代理对象;是否需要代理对象取决于你的配置需要,如是否添加了事务注解或自定义 AOP 切面;如果放弃使用三级缓存,即没有 ObjectFactory,那么就需要将早期的 Bean 放入二级缓存;但问题是,应该将未被代理的 Bean 还是代理的 Bean 放入二级缓存,这只能在属性注入阶段,处理注解时才能分辨。
- 如果直接往二级缓存添加没有被代理的 Bean,那么此时注入给其它对象的 Bean 可能跟最后完全生成的 Bean 是不一样的,因为最后生成使用的是可能代理对象,此时注入的是原始对象,这这种情况是不允许发生的。
- 如果直接往二级缓存添加一个代理 Bean,在不确定是否要使用代理对象的情况下,就有提前暴露代理对象的可能;正常的代理的对象都是初始化后期调用生成的,是基于后置处理器的,若提早的代理就违背了 Bean 定义的生命周期。
Spring 在一个三级缓存放置一个工厂,如果产生循环依赖 ,这个工厂的作用就是判断这个对象是否需要代理,如果否则直接返回,如果是则返回代理对像。
4. 源码验证
在项目中双击 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 管理和依赖注入机制。
以上就是Spring通过三级缓存解决循环依赖问题的过程详解的详细内容,更多关于Spring三级缓存解决循环依赖的资料请关注脚本之家其它相关文章!