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.3
		populateBean(beanName, mbd, instanceWrapper);

		//重要程度:5 bean 实例化+ioc依赖注入完以后的调用 见1.4.2.4
		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
		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 的实例。但这个实例是还没有进行依赖注入的,是提前暴露的,来解决循环依赖这个问题的。

第一次 getBean(CircularRefB) 结束

CircularRefB 的 createBean 结束了。

回到 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 的时候就可以从一级缓存里拿了。

二级缓存有什么用?

在第二次 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 呢?那二级缓存就要用很多次了。

无法解决的循环依赖

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

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

但如果是在构造函数里就需要依赖注入,就放不到三级缓存里了。

发布了130 篇原创文章 · 获赞 233 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_44367006/article/details/104449439