Spring5 源码阅读笔记 (1.4)知识点汇总:解决循环依赖

什么是循环依赖?

如下两段代码所示,Spring 在将 CircularRefA 实例化的时候需要注入 CircularRefB,因此会先实例化 CircularRefB。但实例化 CircularRefB 的时候又会发现需要先实例化 CircularRefA。实例化 CircularRefA 又得先实例化 CircularRefB …

@Component
public class CircularRefA {

    public CircularRefA() {
        System.out.println("============CircularRefA()===========");
    }

    @Autowired
    private CircularRefB circularRefB;
}
@Component
public class CircularRefB {

    public CircularRefB() {
        System.out.println("============CircularRefB()===========");
    }

    @Autowired
    private CircularRefA circularRefA;
}

那么问题是如何解决的呢?

假设我们是先进行 CircularRefA 的实例化。

第一次 getBean(CircularRefA) 开始

参照 Spring5 源码阅读笔记(1.4)finishBeanFactoryInitialization(beanFactory) 完成Bean工厂初始化
实例化的时候会先进行到 getBean(beanName) -> doGetBean(name, null, null, false) -> getSingleton(beanName),

参照Spring5 源码阅读笔记(1.4.1)getSingleton(beanName) 从缓存里拿单例实例,我们知道这一次 getSingleton(beanName) 会先从缓存里拿。这个时候还没有创建实例,拿不到,拿到的是个 null。

@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
	//先从一级缓存拿
	Object singletonObject = this.singletonObjects.get(beanName);
	//如果bean正在创建。堆内存有了,属性还没有DI(依赖注入)
	//现在还没有创建,只是在get,isSingletonCurrentlyInCreation是false,不会走下去
	if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
		synchronized (this.singletonObjects) {
			//从二级缓存中拿
			singletonObject = this.earlySingletonObjects.get(beanName);

			//如果还拿不到,并且允许bean提前引用(解决循环依赖)
			if (singletonObject == null && allowEarlyReference) {
				//从三级缓存中拿到对象工厂
				ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
				if (singletonFactory != null) {
					//从工厂中拿到对象
					singletonObject = singletonFactory.getObject();
					//升级到二级缓存
					this.earlySingletonObjects.put(beanName, singletonObject);
					//删除三级缓存
					this.singletonFactories.remove(beanName);
				}
			}
		}
	}
	return singletonObject;
}

这个时候会继续走前面 doGetBean(name, null, null, false) 的代码,走到

getSingleton(beanName, () -> {
		try {
			return createBean(beanName, mbd, args);
		}
		catch (BeansException ex) {
			// Explicitly remove instance from singleton cache: It might have been put there
			// eagerly by the creation process, to allow for circular reference resolution.
			// Also remove any beans that received a temporary reference to the bean.
			destroySingleton(beanName);
			throw ex;
		}
	});

这里的 getSingleton:

  1. 从一级缓存里拿,没有。
  2. 把 beanName 添加到 singletonsCurrentlyInCreation 容器中,这个时候属于正在创建了。
  3. 执行方法体里面的 createBean,拿到返回值,这个返回值是已经完全创建好的 Bean 实例。
    并且做一个标记表示,拿到的返回值是个新实例。
  4. 把 beanName 从 singletonsCurrentlyInCreation 容器中移除。
  5. 检查标记,发现是新实例,放入一级缓存。
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
	Assert.notNull(beanName, "Bean name must not be null");
	synchronized (this.singletonObjects) {
		//如果一级缓存中有,则直接返回
		Object singletonObject = this.singletonObjects.get(beanName);
		if (singletonObject == null) {
			if (this.singletonsCurrentlyInDestruction) {
				throw new BeanCreationNotAllowedException(beanName,
						"Singleton bean creation not allowed while singletons of this factory are in destruction " +
						"(Do not request a bean from a BeanFactory in a destroy method implementation!)");
			}
			if (logger.isDebugEnabled()) {
				logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
			}

			//把beanName添加到singletonsCurrentlyInCreation容器中
			beforeSingletonCreation(beanName);
			boolean newSingleton = false;
			boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
			if (recordSuppressedExceptions) {
				this.suppressedExceptions = new LinkedHashSet<>();
			}
			try {
				//如果这里有返回值,说明createBean方法返回了Bean实例,已经完全创建成功
				singletonObject = singletonFactory.getObject();
				//这个实例是新实例
				newSingleton = true;
			}
			catch (IllegalStateException ex) {
				// Has the singleton object implicitly appeared in the meantime ->
				// if yes, proceed with it since the exception indicates that state.
				singletonObject = this.singletonObjects.get(beanName);
				if (singletonObject == null) {
					throw ex;
				}
			}
			catch (BeanCreationException ex) {
				if (recordSuppressedExceptions) {
					for (Exception suppressedException : this.suppressedExceptions) {
						ex.addRelatedCause(suppressedException);
					}
				}
				throw ex;
			}
			finally {
				if (recordSuppressedExceptions) {
					this.suppressedExceptions = null;
				}
				//bean创建完成后singletonsCurrentlyInCreation要删除该bean
				afterSingletonCreation(beanName);
			}
			//如果是新实例
			if (newSingleton) {
				//放入一级缓存
				addSingleton(beanName, singletonObject);
			}
		}
		return singletonObject;
	}
}

那么问题来了,createBean 能达到返回值吗?换句话说 CircularRefA 的实例能创建成功吗?
这里走到了上面说的第 3 步,下面我们看看能不能拿到返回值。

参考 Spring5 源码阅读笔记(1.4.2)createBean(beanName, mbd, args) 创建Bean

在 createBean(beanName, mbd, args) 方法里走到 doCreateBean(beanName, mbdToUse, args),
这个方法做了这么几步:

  1. 根据构造方法创建一个没有依赖注入的 CircularRefA 实例。这是可以做到的。
  2. 扫描到有 @AutoWired 注解的 CircularRefB,将相关信息封装成 InjectionMetadata。
  3. 因为允许提前暴露 bean(属性还没有注入,就被使用),将 beanName 放到了三级缓存
  4. 依赖注入,优先实例化依赖对象 CircularRefB。
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
	throws BeanCreationException {

	//对Bean实例的封装
	BeanWrapper instanceWrapper = null;
	if (mbd.isSingleton()) {
		instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
	}
	if (instanceWrapper == null) {
		//重要程度:5 创建实例 见1.4.2.1
		instanceWrapper = createBeanInstance(beanName, mbd, args);
	}
	final 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 {
				//CommonAnnotationBeanPostProcessor  支持了@PostConstruct,@PreDestroy,@Resource注解
				//AutowiredAnnotationBeanPostProcessor 支持 @Autowired,@Value注解
				//BeanPostProcessor接口的典型运用,这里要理解这个接口
				//对类中注解的装配过程
				//重要程度5 见1.4.2.2
				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.
	//是否	单例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");
		}
		//重要程度:5 添加三级缓存 见1.4.2.3
		addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
	}

	// Initialize the bean instance.
	Object exposedObject = bean;
	try {
		//重要程度:5 依赖注入的核心方法 见1.4.2.4
		populateBean(beanName, mbd, instanceWrapper);

		//重要程度:5 bean 实例化+ioc依赖注入完以后的调用 见1.4.2.5
		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) {
		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 " +
							"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
				}
			}
		}
	}

	// Register bean as disposable.
	try {
		//注册bean销毁时的类DisposableBeanAdapter 见1.4.2.6
		registerDisposableBeanIfNecessary(beanName, bean, mbd);
	}
	catch (BeanDefinitionValidationException ex) {
		throw new BeanCreationException(
				mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
	}

	return exposedObject;
}

走到这里就回去实例化 CircularRefB

第一次 getBean(CircularRefB) 开始

对照之前的步骤:
getBean(beanName) -> doGetBean(name, null, null, false) -> getSingleton(beanName) 缓存里没有,回去 getSingleton:

  1. 从一级缓存里拿,没有
  2. 把 beanName 添加到 singletonsCurrentlyInCreation 容器中,这个时候属于正在创建了
  3. 执行方法体里面的 createBean

createBean(beanName, mbd, args) -> doCreateBean(beanName, mbdToUse, args):

  1. 根据构造方法创建一个没有依赖注入的 CircularRefB 实例。这是可以做到的。
  2. 扫描到有 @AutoWired 注解的 CircularRefA,将相关信息封装成 InjectionMetadata。
  3. 因为允许提前暴露 bean(属性还没有注入,就被使用),将 beanName 放到了三级缓存
  4. 依赖注入,优先实例化依赖对象 CircularRefA。

又到了 CircularRefA 的 getBean 操作

第二次 getBean(CircularRefA) 开始、结束

getBean(beanName) -> doGetBean(name, null, null, false) -> getSingleton(beanName) 从缓存里拿
注意这里的从缓存里拿的操作:

  1. 从三级工厂里拿到了 singletonFactory,继而拿到了 CircularRefA 的实例。
  2. 将 CircularRefA 的实例增至二级缓存,并从三级缓存中移除。
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
	//先从一级缓存拿
	Object singletonObject = this.singletonObjects.get(beanName);
	//bean 正在创建,跟上次不同,这次会进入if代码块
	if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
		synchronized (this.singletonObjects) {
			//从二级缓存中拿,也没有
			singletonObject = this.earlySingletonObjects.get(beanName);
			//还拿不到,并且允许bean提前引用
			if (singletonObject == null && allowEarlyReference) {
				//从三级缓存中拿到对象工厂 singletonFactory ,确实有
				ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
				if (singletonFactory != null) {
					//从工厂中拿到对象
					singletonObject = singletonFactory.getObject();
					//升级到二级缓存
					this.earlySingletonObjects.put(beanName, singletonObject);
					//删除三级缓存
					this.singletonFactories.remove(beanName);
				}
			}
		}
	}
	return singletonObject;
}

这里就直接返回了一个 CircularRefA 的实例。但这个实例是还没有进行依赖注入的(CircularRefB == null ),是提前暴露的,来解决循环依赖这个问题的。

第一次 getBean(CircularRefB) 结束

CircularRefB 的 createBean 结束了。

回到 CircularRefB 的 getSingleton 的第3步:

  1. 从 createBean 里拿到了返回值。并且做一个标记表示,拿到的返回值是个新实例。
  2. 把 beanName 从 singletonsCurrentlyInCreation 容器中移除。
  3. 检查标记,发现是新实例,放入一级缓存。

第一次 getBean(CircularRefA) 结束

回到 getSingleton 的第3步:

  1. 从 createBean 里拿到了返回值。并且做一个标记表示,拿到的返回值是个新实例。
  2. 把 beanName 从 singletonsCurrentlyInCreation 容器中移除。
  3. 检查标记,发现是新实例,放入一级缓存。

至此 CircularRefA 的实例化结束了。CircularRefB 里的 CircularRefA 属性才不为 null。CircularRefA 里的 CircularRefB 属性也不为 null 了。

CircularRefA 的实例化结束了,顺便也把 CircularRefB 也实例化了。当 Spring 再去实例化 CircularRefB 的时候就可以从一级缓存里拿了。

三级缓存有什么用?

为什么三级缓存要存一个单例工厂,而不直接存单例对象呢?

看了 Spring5 源码阅读笔记(1.4.2.3)addSingletonFactory(beanName, ()->getEarlyBeanReference(beanName, mbd, bean)) 这篇文章就知道了,我们调用 singletonFactory.getObject() 会执行 getEarlyBeanReference 方法,拿到它的返回值。而 getEarlyBeanReference 会用所有的 SmartInstantiationAwareBeanPostProcessor 的 getEarlyBeanReference 方法去处理 bean,拿到返回值。

实现了 SmartInstantiationAwareBeanPostProcessor 之一的就是 AbstractAutoProxyCreator,它会尝试地去生成代理对象。如果有的话,会返回代理对象。

二级缓存有什么用?

在第二次 getBean(CircularRefA) 里,有一步是将 CircularRefA 的实例增至二级缓存,并从三级缓存中移除。但这一步和后面的操作不再产生关联,那么二级缓存有用吗?

在这个例子中确实体现不了作用。但如果是下面这个例子呢?

A 依赖 B、C
B 依赖 A
C 依赖 A

分析一下:

  1. A 先实例化,A 进三级缓存,需要依赖注入 B 和 C
  2. 实例化 B,B 进三级缓存,需要依赖注入 A
  3. A 第二次实例化,从三级缓存拿,并升至二级缓存
  4. B 拿到 A,B 实例化完成
  5. 实例化 C,C 进三级缓存,需要依赖注入 A
  6. A 第三次实例化,从二级缓存拿
  7. C 拿到 A,C 实例化完成
  8. A 拿到 B 和 C,A 实例化完成

上面的例子,只从二级缓存里拿了一次。

但假如 A 要依赖很多类,而这些类又依赖 A 呢?那二级缓存就要用很多次了。

而三级缓存我们分析过了,每次调用它的 getObject() 方法,都会调用所有的 SmartInstantiationAwareBeanPostProcessor 的 getEarlyBeanReference 去处理。
很明显性能较差。

无法解决的循环依赖

注意:如果有两个类 A 依赖 B,B 依赖 A。但是,它们互相依赖的是在构造函数里的入参。也就是在构造方法上打上了 @Autowired,入参是另一个类的引用。这种循环依赖是无法解决的。

原因很简单:
当通过构造函数创建了一个没有注入的实例后,是先放入三级缓存,再进行依赖注入的。

但如果是在构造函数里就需要依赖注入,就没有三级缓存什么事了。然而,三级缓存是解决循环依赖的关键。

©️2020 CSDN 皮肤主题: 岁月 设计师: pinMode 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值