👺 循环依赖

吞佛童子2022年6月20日
  • frame
  • Spring
  • 循环依赖
大约 5 分钟

👺 循环依赖

1. 前提

  1. 出现循环依赖的 Bean 必须要是 单例 (singleton)
    • prototype 下,Spring 直接抛出异常
      • 假设 AB 均为 prototype && AB 循环依赖
      • 当 实例化 A1 时,发现依赖 B,然后 实例化 B1 时,发现依赖 A,然后 实例化 A2,发现依赖 B,然后 实例化 B2,...
      • 无限循环,该问题无法解决,还会导致系统崩溃
  2. 依赖注入的方式不能全是构造器注入的方式
    • bean 的生命周期中的 实例化 & 属性赋值 是 两个阶段,只有在 属性赋值 这一阶段才可能有操作空间,
    • 只有 实例化 完成后才可能将 半成品对象 放入 三级缓存

2. 可以解决的情况

这里假设 A 先进行创建工作

A 注入 B 方式B 注入 A 方式是否可以解决
属性注入属性注入😋 😋
构造器注入构造器注入☹️
setter()构造器注入😋 😋
构造器注入setter()☹️

3. 三级缓存

  1. Map<String,Object> singletonObjects
    • 一级缓存
    • 保存已经 实例化、属性赋值、初始化 完成的 bean 单例
  2. Map<String,Object> earlySingletonObjects
    • 二级缓存
    • 保存完成 实例化 的 bean 单例,这些 bean 也称为 早期曝光 对象
  3. Map<String,ObjectFactory<?>> singletonFactories
    • 三级缓存
    • 保存 bean 的创建工厂,便于以后发生循环依赖时可以创建代理对象 or 原本对象

是否可用二级缓存?

  • 二级缓存,只能是 一级缓存 + 二级缓存
    • 若为 一级缓存 + 三级缓存,在获取 singleton 时,发现一级缓存不存在,直接通过 工厂创建
    • 此时无法确保工厂之前是否已经生成该 半成品 类
      • 若生成 半成品类 后,从 三级缓存中删除,那么除了初次工厂制造,后续无法工厂创建,但我们也无法得到 工厂创建 的半成品类,因为不知道保存在哪里
      • 若生成 半成品类 后,不从 三级缓存中删除,那么并发下,发现一级缓存不存在,工厂可能创建多个 半成品类,破坏单例模式,同时,生成的 半成品类 也没有保存在特定结构中
    • 若为 一级缓存 + 二级缓存,在创建 singleton 时,直接放入 二级缓存
  • 没有代理对象生成的情况下:
    • 可以解决循环依赖问题
    • 在获取 singleton 时,发现一级缓存不存在,去二级缓存查找,找到即可直接返回
  • 有代理对象生成的情况下:
    • 无法解决循环依赖问题
    • 在创建 singleton 时,直接放入 二级缓存,此时放入的是 半成品普通对象,而非该类的 代理对象
    • 在获取 singleton 时,发现一级缓存不存在,去二级缓存查找,找到的是 半成品普通对象
    • 获取到之后,进行后续的 初始化 过程,在 BeanPostProcessor 过程中会转变成 代理对象,覆盖掉原有 半成品普通对象
    • 从而可能导致多线程下获取到的 bean 对象不一致,有的线程获取的是 普通对象,有的获取的是 代理对象

4. 源码相关

1) singleton 创建时

	/**
	 * 主要包含以下步骤:
	 * 1. 实例化,创建 bean 实例
	 *    1.1 实例化完成后,如果满足 提前暴露 条件,需要将该 bean 放入 三级缓存
	 * 2. 填充属性
	 * 3. bean 初始化
	 * 
	 * @param beanName the name of the bean
	 * @param mbd the merged bean definition for the bean
	 * @param args explicit arguments to use for constructor or factory method invocation
	 * @return a new instance of the bean
	 * @throws BeanCreationException if the bean could not be created
	 * @see #instantiateBean
	 * @see #instantiateUsingFactoryMethod
	 * @see #autowireConstructor
	 */
	protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {

		// 实例化、...
		// 从这里可以看出,要放入三级缓存前,生产的 bean 必须已经完成实例化过程
		
		// ------ 解决循环依赖问题 -----------
		// 判断是否可以提前暴露
		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");
			}
			// 将该 bean 放入 三级缓存
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}
		
		// 属性赋值、初始化...	
	}			

2) singleton 获取时

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry { 
    // ......
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256); // 一级缓存
    private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap(16); // 二级缓存
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16); // 三级缓存

	@Nullable
	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		// 1. 尝试从一级缓存中获取
		Object singletonObject = this.singletonObjects.get(beanName);
		//  一级缓存中不存在,且 为单例 bean 的创建过程中
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
		    // 2. 尝试从二级缓存中获取
			singletonObject = this.earlySingletonObjects.get(beanName);
			// 二级缓存不存在,且 允许提前暴露
			if (singletonObject == null && allowEarlyReference) {
			   // 锁住一级缓存
				synchronized (this.singletonObjects) {
					// 再次确认,有点类似 单例模式双重检验加锁
					singletonObject = this.singletonObjects.get(beanName);
					if (singletonObject == null) { // 一级缓存不存在
						singletonObject = this.earlySingletonObjects.get(beanName);
						if (singletonObject == null) { // 二级缓存也不存在
						   // 3. 尝试获取三级缓存的 bean 工厂
							ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
							// 三级缓存中存在该 bean 的创建工厂,因为之前 createBean() 中发现可能发生循环依赖就会创建三级缓存
							if (singletonFactory != null) { 
							    // 通过该 bean 的创建工厂 创建该 单例 bean
							    // 这里创建的 bean 有两种:
							    // 若 bean 被 AOP 代理,则创建的是 bean 代理后的对象
							    // 若 baen 没有被 AOP 代理,则创建的是 bean 实例化后的对象
								singletonObject = singletonFactory.getObject();
								// 将创建的 bean 放入二级缓存,表示已经实例化,但还没有初始化
								this.earlySingletonObjects.put(beanName, singletonObject);
								// 该创建工厂已经完成创建工作,创建一次就不需要了,将其从 三级缓存中删除
								this.singletonFactories.remove(beanName);
							}
						}
					}
				}
			}
		}
		return singletonObject; // 只要一级缓存 | 二级缓存中任何存在该单例 bean,直接返回
	}
}

5. 解决流程


上次编辑于: 2022/10/10 下午8:43:48
贡献者: liuxianzhishou