Spring中怎么解决循环依赖?

前文

在写Spring之getBean
的时候提到过在这个过程中要解决循环依赖。

什么是循环依赖?

A类依赖B类,B类依赖A类。 这就是循环依赖。
如下就是一段在Spring中会造成循环依赖的代码

@Component
public class A {
    private B b;

    @Autowired
    public A(B b) {
        this.b = b;
    }
}
@Component
public class B {
    private A a;
    @Autowired
    public B(A a) {
        this.a = a;
    }
}

启动后会报错:error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?

哪儿在抛错?

跟踪源码查看一下为何会报错。

Spring之getBean
知道了启动的时候会去创建所有非lazy的单实例bean

主要跟踪getSingleton:
1.没有已经加载好的实例,那么准备创建了。
2.创建前先使用beforeSingletonCreation(beanName);去检查是否【正在创建】,抛出循环依赖的异常。

	public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
		Assert.notNull(beanName, "Bean name must not be null");
		synchronized (this.singletonObjects) {
		//关注:从【一级缓存】中拿【已经加载好的】bean实例
			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 + "'");
				}
				//关注:先检查一下是否已经在创建。 就是 在这里检查了循环依赖。如果没有就添加【正在创建】标志
				beforeSingletonCreation(beanName);
				boolean newSingleton = false;
				boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
				if (recordSuppressedExceptions) {
					this.suppressedExceptions = new LinkedHashSet<>();
				}
				try {
					//关注:创建实例过程--调用createBean
					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;
					}
					//关注:移出【正在创建】标志
					afterSingletonCreation(beanName);
				}
				if (newSingleton) {
					addSingleton(beanName, singletonObject);
				}
			}
			return singletonObject;
		}
	}

但是为何会抛错呢?

debug跟一下。 发现在第getBean(“a”)的时候,会有如下步骤:
1.getBean(“a”)。
2.标记A【正在创建】。
3.得到A的构造函数,尝试去创建A的实例。
4.在创建A的实例之前,要得到构造函数的参数B的实例。
5.getBean(“b”)。
6.标记B【正在创建】。
7.重复步骤2-3. 不过此时是得到B的构造函数,并尝试获取A的实例。
8.getBean(“a”) 。ps:注意,这里是第二次getBean(“a”)了。
9.再次标记A【正在创建】中。—标记失败,抛出异常。

具体过程参考下图:
ps:网图改了一下
在这里插入图片描述
由此,知道为何构造注入会引起循环依赖的时候抛错。
如果对这个注入过程不熟悉的,可以参考我之前的Spring之getBean

那么如何避免抛错呢?

1.允许循环依赖(默认是允许的)。①
2.使用非构造注入,如@Autowired写在成员变量或者setter方法上。
3.要有无参构造函数,或者是不包含被依赖的成员变量的构造函数(避免在一开始注入的时候就需要一个拥有完整成员变量的B)。

例如之前的例子:

开始A实例的创建
1.getBean(“a”)->标记A【正在创建中】。
2.创建A的实例的时候,用无参构造创建一个A的实例a。
3.将半成品(无成员变量)a放到三级缓存中。②
4.populate的时候,尝试注入成员变量b。


开始B的实例创建

  5.getBean(“b”)创建一个B的实例>标记B【正在创建中】
  6.B通过createBeanInstance创建实例后,
  7.将半成品(无成员变量)b放到三级缓存中。
  8. populate去注入成员变量a.
  9.getBean(“a”)->getSingleton(“a”, true)->从一级缓存(singletonObjects)中取->取不到则从二级缓存(earlySingletonObjects)中取->取不到则从三级缓存(singletonFactories)中取->从三级缓存中取出ObjectFactory得到A的实例a,将a放入二级缓存,并将ObjectFactory从三级缓存中移出。③
  10.得到了a的实例(半成品),注入到b中。继续走完B创建。
b实例的创建完成


继续A实例的创建
11.将完成品B的实例b,注入到A中。 继续走完A创建。


非构造注入整个流程
ps:偷来的图
在这里插入图片描述

①不允许循环依赖当然要报错
在这里插入图片描述

②将半成品(无成员变量)a放到三级缓存中

将半成品(无成员变量)a放到三级缓存中在这里插入图片描述

③从三级缓存中取出ObjectFactory得到A的实例a,将a放入二级缓存,并将ObjectFactory从三级缓存中移出

在这里插入图片描述

为何是三级缓存?

个人认为,第三级缓存的作用, 是留了一个钩子,方便【获取半成品实例】的时候做一些定制。
如下源码:实现SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference可以完成该定制。

	protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
		Object exposedObject = bean;
		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
					SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
					exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
				}
			}
		}
		return exposedObject;
	}

参考资料

https://blog.csdn.net/f641385712/article/details/92801300
https://blog.csdn.net/weixin_42228338/article/details/97163101

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章