1. Spring解决循环依赖问题

一、前面的知识

1. 什么是循环依赖?

  • 循环依赖就是循环引用,指两个或多个bean互相持有对方。
  • 通常来说,如果问Spring内部如何解决循环依赖,一定是默认的单例Bean中,属性互相引用的场景
  • 原型(Prototype)的场景是不支持循环依赖的,存在循环依赖会抛出异常
  • 原因很好理解,创建新的A时,发现要注入原型字段B,又创建新的B发现要注入原型字段A...
  • 构造器循环依赖会抛出异常
  • 通常来说,如果问Spring内部如何解决循环依赖,一定是默认的单例Bean中通过 @Autowired或者 setter方法进行循环依赖

2. 原始对象、代理对象

(1)未使用 AOP时,Spring管理的是原始对象
(2) 使用 AOP 时
  • AOP 生成一个代理对象,内部有一个target属性指向原始对象。

  • Spring并没有给代理对象进行属性填充。只是给原始对象进行了属性填充。放入单例池中的,肯定是一个代理对象。所以注入的属性也是代理对象

3. Spring 的AOP中步骤,

    1. 实例化对象
    2. new 一个代理工厂
    3. 把对象设置到工厂中去
    4. 给工厂设置一些代理的逻辑
    5. 获取一个代理对象
    6. 执行代理对象的方法

二、Spring 循环依赖问题分析

1 (1). AService的Bean生命周期

  1. 实例化出来一个对象(new AService())
  2. 填充bService属性 (去单例池查找BService对象-->没有找到-->创建BService对象)
  3. 填充其他属性
  4. AOP
  5. 把对象放入单例池

(2). BService的Bean生命周期

  1. 实例化出来一个对象(new BService())
  2. 填充aService属性 ( 去单例池查找AService对象-->没有找到-->创建AService对象)
  3. 填充其他属性
  4. AOP
  5. 把对象放入单例池

2 (1). AService的Bean生命周期

  1. 实例化出来一个对象(new AService() --》 原始对象 --》TempMap<aService: 原始对象> )
  2. 填充bService属性 ( 去单例池查找BService对象-->没有找到-->创建BService对象)
  3. 填充其他属性
  4. AOP
  5. 把对象放入单例池

(2). BService的Bean生命周期

  1. 实例化出来一个对象(new BService())

  2. 填充aService属性 (去单例池查找AService对象 --》没有找到 --》去TempMap查找 --》获得AService原始对象)

  3. 填充其他属性

  4. AOP

  5. 把对象放入单例池

存在的问题:

    1. bService填充aService属性时,从tempMap中拿出来的是一个原始对象。而最终放入单例池中的是一个代理对象,最终填充aService属性的也需要是代理对象。
    1. 如何判断对象是否出现了循环依赖?

      上式BService去单例池查找AService对象,没有找到。即可知AService出现了循环依赖,因为AService创建过程中才导致BService进行创建,而BService创建时发现AService自己还在创建过程中。

3(1). AService的Bean生命周期

  1. creatingSet.add("aService")

  2. 实例化出来一个对象(new AService() --》原始对象 --》TempMap<aService: 原始对象> )

  3. 填充bService属性 ( 去单例池查找BService对象-->没有找到-->创建BService对象)

  4. 填充其他属性

  5. AOP

  6. 把对象放入单例池

  7. creatingSet.remove("aService")

(2). BService的Bean生命周期

  1. 实例化出来一个对象(new BService())
  2. 填充aService属性 (去单例池查找AService对象 --》没有找到,且AService正在创建中 --》去TempMap查找得到AService原始对象 --》对AService进行AOP--》获得AService代理对象)
  3. 填充其他属性
  4. AOP
  5. 把对象放入单例池

(3). CService的Bean生命周期

  1. 实例化出来一个对象(new CService())
  2. 填充aService属性 (去单例池查找AService对象 --》没有找到,且AService正在创建中 --》去TempMap查找得到AService原始对象 --》对AService进行AOP--》获得AService代理对象)
  3. 填充其他属性
  4. AOP
  5. 把对象放入单例池

存在问题

    1. 原始对象属性还未填充,即代理对象还不完整。所以 (2)的第二步不能将AService对象放入单例池中,且 AService其他属性填充也会创建 BService对象。
    2. AService存在CService属性时,又会创建一个AService代理对象

总结: 所以,依赖的对象存在于creatingSet即表示此对象存在循环依赖

4(1). AService的Bean生命周期

  1. creatingSet.add("aService")

  2. 实例化出来一个对象(new AService() --》原始对象 --》TempMap<aService: 原始对象> )

  3. 填充bService属性 ( 去单例池查找BService对象-->没有找到-->创建BService对象)

  4. 填充其他属性

  5. AOP

    4.1从第二级缓存Map中取得AService代理对象

  6. 把对象放入单例池

  7. creatingSet.remove("aService")

(2). BService的Bean生命周期

  1. 实例化出来一个对象(new BService())
  2. 填充aService属性 (去单例池查找AService对象 --》 没有 --》从二级缓存进行查找 --》没有找到,且AService正在创建中 --》去TempMap查找得到AService原始对象 --》对AService进行AOP--》获得AService代理对象 --》代理对象放入第二级缓存Map )
  3. 填充其他属性
  4. AOP
  5. 把对象放入单例池

(3). CService的Bean生命周期

  1. 实例化出来一个对象(new CService())
  2. 填充aService属性 (去单例池查找AService对象 --》 没找到,去二级缓存中查找--》获得AService代理对象 )
  3. 填充其他属性
  4. AOP
  5. 把对象放入单例池

总结:

  1. 二级缓存,保证了提前进行AOP的代理对象是唯一的。
  2. 步骤(2)中,执行 TempMap中的 lambda表达式就是进行 AOP 操作。定义AService时候, lambda还是 AService生命周期内,可以多存储参数。
    • 第一级缓存: 单例池
    • 第二级缓存: Map(beanName,不完整的代理对象)
    • 第三级缓存:TempMap(beanName,原始对象)

三、Spring循环依赖处理流程

1 (1). AService的Bean生命周期

  1. creatingSet.add("aService")

  2. 实例化出来一个对象(new AService() --》原始对象 --》singletonFactories<aService: lambda(原始对象)> )

  3. 填充bService属性 ( 去单例池查找BService对象-->没有找到-->创建BService对象)

  4. 填充其他属性

  5. AOP(判断是否存在AService代理对象?)

    4.1从第二级缓存Map中取得AService代理对象

  6. 把对象放入单例池

  7. creatingSet.remove("aService")

(2). BService的Bean生命周期

  1. 实例化出来一个对象(new BService())
  2. 填充aService属性 (去单例池查找AService对象 --》 没有 --》从二级缓存进行查找 --》没有找到,且AService正在创建中 --》去singletonFactories查找得到lambda(AService原始对象) --》执行lambda进行AOP--》获得AService代理对象 --》代理对象放入第二级缓存Map )
  3. 填充其他属性
  4. AOP
  5. 把对象放入单例池

2. 三个缓存源码定义 。

源码中的方法:getSingleton(String beanName, boolean allowEarlyReference)
第一级缓存: 单例池 singletonObject = new HashMap(beanName: Bean完整的代理对象)
第二级缓存: earlySingletonObject = new HashMap(beanName: Bean不完整的代理对象)
第三级缓存 :singletonFactories = Map(beanName, lambda(原始对象)) 。

3. 流程图

![image-20210105205234660](/Users/renren/Library/Application Support/typora-user-images/image-20210105205234660.png)

四、问题解答

  1. 源码中加synchronized锁的意义?
      @Nullable
	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		// Quick check for existing instance without full singleton lock
		Object singletonObject = this.singletonObjects.get(beanName);
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			singletonObject = this.earlySingletonObjects.get(beanName);
			if (singletonObject == null && allowEarlyReference) {
				synchronized (this.singletonObjects) {
					// Consistent creation of early reference within full singleton lock
					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) {
								// 1. 执行lambda表达式,进行AOP,得到代理对象
								singletonObject = singletonFactory.getObject();
								// 2. 代理对象放入2级缓存
								this.earlySingletonObjects.put(beanName, singletonObject);
								// 3. 3级缓存中移除此对象
								this.singletonFactories.remove(beanName);
							}
						}
					}
				}
			}
		}
		return singletonObject;
	}

背景:

二级缓存中的aService对象是三级缓存中的lambda表达式生成出来的,

他们是成对的,二级缓存中存在代理对象,则三级缓存中不应该存在lambda表达式;

或者说,三级缓存中存在lambda表达式,则二级缓存中不应当有该代理对象

解答:

  1. 3级缓存中的 value是一个lambda表达式,一执行就是进行AOP,得到代理对象,所以lambda表达式应当只执行一次,且执行完毕后从3级缓存中进行移除,以防止其他的代码又拿出来执行了

  2. 在第2、第3级缓存只能有一个地方存在操作对象,要么是lambda表达式(三级缓存),要么是lambda执行后的代理对象(二级缓存)。这是原子性的,为了对高并发情况进行控制,加锁进行同步。

  3. 1级缓存定义为 concurrentHashMap。 2级、3级缓存定义为简单的HashMap,是因为 2、3级缓存是成对出现的,哪怕是定义成concurrentHashMap,也要加锁保持两个Map的操作的原子性

2. AService第4步, 如何判断是否存在AService代理对象?

答: 从二级缓存中进行查找AService代理对象。 可行,但是 AOP可以看做是一种插件,缓存是一个bean工厂。这两个是相互独立的,要互相解耦,所以Spring不是直接这么做的

上式AService,存在循环依赖,所以步骤2(具体为注入bService属性过程中)会执行lambda表达式,即执行了getEarlyBeanReference(Object bean, String beanName) 方法, 提前获取代理对象步骤4会执行postProcessAfterInitialization(),根据返回的是原始对象还是代理对象,再从二级缓存进行拿取

	@Override
	public Object getEarlyBeanReference(Object bean, String beanName) {
		Object cacheKey = getCacheKey(bean.getClass(), beanName); // 获取beanName, aService
		// lambda表达式执行时,AOP执行前的原始对象存入earlyProxyReferences Map中
		this.earlyProxyReferences.put(cacheKey, bean);
               // 真正进行 AOP
		return wrapIfNecessary(bean, beanName, cacheKey);
	}

	@Override
	public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
		if (bean != null) {
			Object cacheKey = getCacheKey(bean.getClass(), beanName);
			// earlyProxyReferences 存的是提前进行了AOP的bean, beanName:AOP之前的对象
			if (this.earlyProxyReferences.remove(cacheKey) != bean) {
				return wrapIfNecessary(bean, beanName, cacheKey);
			}
		}
		return bean;
	}
      
      // 实际创建对象
      protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {

		// Instantiate the bean.
		// 1. 初始化
		BeanWrapper instanceWrapper = null;
		if (mbd.isSingleton()) {
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
		}
		// 2. 实例化
		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");
			}
			// 此时的bean还没有完成属性注入,是一个非常简单的对象
			// 构造一个对象工厂,添加到singletonFactory中
			// TODO: 执行到这一行时, lambda表达式并未执行, lambda表达式可以记录原始对象的一些属性
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}

		// Initialize the bean instance.
		// 对象已经暴露出去了
		Object exposedObject = bean;
		try {
			// 3. 填充属性 @Autowired
			populateBean(beanName, mbd, instanceWrapper);

			// 4. 初始化并执行 BeanPostProcessor
			// 如果进行了AOP,则返回的exposedObject是代理对象。如果没进行AOP,则返回的是原始对象
			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) {
			// 解决循环依赖时,当 AService属性注入完毕后,从 getSingleton中获取 AService进行AOP之后的代理对象
			// getSingleton: 从二级缓存中取对象
			Object earlySingletonReference = getSingleton(beanName, false);
			if (earlySingletonReference != null) {
				if (exposedObject == bean) {
					// 如果提前暴露的对象和经过了完整生命周期的对象相等,则把代理对象赋值给exposedObject
					exposedObject = earlySingletonReference;
				}
				else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
					// 如果提前暴露的对象和经过了完整生命周期的对象不相等。
					// allowRawInjectionDespiteWrapping 表示处于循环依赖过程中
					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;
	}
posted @ 2021-01-06 23:53  skillking2  阅读(341)  评论(0)    收藏  举报