什么是循环依赖?
如下两段代码所示,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:
- 从一级缓存里拿,没有。
- 把 beanName 添加到 singletonsCurrentlyInCreation 容器中,这个时候属于正在创建了。
- 执行方法体里面的 createBean,拿到返回值,这个返回值是已经完全创建好的 Bean 实例。
并且做一个标记表示,拿到的返回值是个新实例。 - 把 beanName 从 singletonsCurrentlyInCreation 容器中移除。
- 检查标记,发现是新实例,放入一级缓存。
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),
这个方法做了这么几步:
- 根据构造方法创建一个没有依赖注入的 CircularRefA 实例。这是可以做到的。
- 扫描到有 @AutoWired 注解的 CircularRefB,将相关信息封装成 InjectionMetadata。
- 因为允许提前暴露 bean(属性还没有注入,就被使用),将 beanName 放到了三级缓存。
- 依赖注入,优先实例化依赖对象 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:
- 从一级缓存里拿,没有
- 把 beanName 添加到 singletonsCurrentlyInCreation 容器中,这个时候属于正在创建了
- 执行方法体里面的 createBean
createBean(beanName, mbd, args) -> doCreateBean(beanName, mbdToUse, args):
- 根据构造方法创建一个没有依赖注入的 CircularRefB 实例。这是可以做到的。
- 扫描到有 @AutoWired 注解的 CircularRefA,将相关信息封装成 InjectionMetadata。
- 因为允许提前暴露 bean(属性还没有注入,就被使用),将 beanName 放到了三级缓存。
- 依赖注入,优先实例化依赖对象 CircularRefA。
又到了 CircularRefA 的 getBean 操作
第二次 getBean(CircularRefA) 开始、结束
getBean(beanName) -> doGetBean(name, null, null, false) -> getSingleton(beanName) 从缓存里拿
注意这里的从缓存里拿的操作:
- 从三级工厂里拿到了 singletonFactory,继而拿到了 CircularRefA 的实例。
- 将 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步:
- 从 createBean 里拿到了返回值。并且做一个标记表示,拿到的返回值是个新实例。
- 把 beanName 从 singletonsCurrentlyInCreation 容器中移除。
- 检查标记,发现是新实例,放入一级缓存。
第一次 getBean(CircularRefA) 结束
回到 getSingleton 的第3步:
- 从 createBean 里拿到了返回值。并且做一个标记表示,拿到的返回值是个新实例。
- 把 beanName 从 singletonsCurrentlyInCreation 容器中移除。
- 检查标记,发现是新实例,放入一级缓存。
至此 CircularRefA 的实例化结束了。CircularRefB 里的 CircularRefA 属性才不为 null。CircularRefA 里的 CircularRefB 属性也不为 null 了。
CircularRefA 的实例化结束了,顺便也把 CircularRefB 也实例化了。当 Spring 再去实例化 CircularRefB 的时候就可以从一级缓存里拿了。
二级缓存有什么用?
在第二次 getBean(CircularRefA) 里,有一步是将 CircularRefA 的实例增至二级缓存,并从三级缓存中移除。但这一步和后面的操作不再产生关联,那么二级缓存有用吗?
在这个例子中确实体现不了作用。但如果是下面这个例子呢?
A 依赖 B、C
B 依赖 A
C 依赖 A
分析一下:
- A 先实例化,A 进三级缓存,需要依赖注入 B 和 C
- 实例化 B,B 进三级缓存,需要依赖注入 A
- A 第二次实例化,从三级缓存拿,并升至二级缓存
- B 拿到 A,B 实例化完成
- 实例化 C,C 进三级缓存,需要依赖注入 A
- A 第三次实例化,从二级缓存拿
- C 拿到 A,C 实例化完成
- A 拿到 B 和 C,A 实例化完成
上面的例子,只从二级缓存里拿了一次。
但假如 A 要依赖很多类,而这些类又依赖 A 呢?那二级缓存就要用很多次了。
无法解决的循环依赖
注意:如果有两个类 A 依赖 B,B 依赖 A。但是,它们互相依赖的是在构造函数里的入参。也就是在构造方法上打上了 @Autowired,入参是另一个类的引用。这种循环依赖是无法解决的。
原因很简单:
当通过构造函数创建了一个没有注入的实例后,是先放入三级缓存,再进行依赖注入的。
但如果是在构造函数里就需要依赖注入,就放不到三级缓存里了。