如果 class A 中依赖了 class B并且class B 中也依赖了class A,形成一个闭环就会产生循环依赖的问题。
构造器注入方式的循环依赖,无法解决;
Setter注入方式的循环依赖,解决方式:
在 AbstractBeanFactory 的 doGetBean 方法中,会首先通过缓存中去查找单例 Bean 对象。调用 getSingleton 方法,源码如下:
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//检查缓存中是否存在实例
Object singletonObject = this.singletonObjects.get(beanName);
//如果单例bean为空但工厂内正在加载
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
//如果为空,则锁定全局变量并进行处理
synchronized (this.singletonObjects) {
//如果此bean正在加载则不处理
singletonObject = this.earlySingletonObjects.get(beanName);
//是否允许从提前曝光缓存singletonFactories中通过getObject拿到对象,解决属性注入循环依赖问题的关键
if (singletonObject == null && allowEarlyReference) {
//当某些方法需要提前初始化的时候则会调用addSingletonFactory方法将对应的ObjectFactory初始化策略存储在singletonFactories
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
//调用预先设定的getObject方法
singletonObject = singletonFactory.getObject();
//记录在缓存中,earlySingletonObjects和singletonFactories互斥
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
从这个方法上我们可以看到 Spring 使用三级缓存解决循环依赖问题,每个缓存存储实例对象引用的不同生命周期:
在通过上一篇Spring 之 DI 详解,我们知道 Bean 初始化的三个重要步骤为:
我们来看看实例化完之后在什么时候存储到 singletonFactories 缓存中,在 AbstractAutowireCapableBeanFactory.doCreateBean() 方法中。
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) {
//根据指定bean使用对应的策略创建实例对象,如:指定的工厂方法、根据参数选择构造函数、默认无参构造方法
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
//...
// 向容器中缓存单例模式的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");
}
//为避免后期循环依赖,尽早持有对象的引用
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
//...
}
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
//提前曝光实例化完成暂未进行属性注入的bean对象,以解决属性注入的循环引用问题
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
可以看到在 createBeanInstance 方法实例化对象之后(此时还未进行依赖注入)就会暴露到 singletonFactories 三级缓存集合中,以解决属性注入的循环引用问题。
在 AbstractBeanFactory 的 doGetBean 方法中,调用 createBean 方法
//创建单例模式的Bean的实例对象
if (mbd.isSingleton()) {
//调用匿名内部类创建Bean实例对象,创建Bean实例对象,并且注册给所依赖的对象
sharedInstance = getSingleton(beanName, () -> {
try {
//创建一个指定Bean实例对象,如果有父级继承,则合并子类和父类的定义
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.
//从单例模式的Bean缓存中清除实例对象
destroySingleton(beanName);
throw ex;
}
});
//获取给定Bean的实例对象,主要完成FactoryBean获取实例化对象过程
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
继续跟进 DefaultSingletonBeanRegistry.getSingleton() 方法,源码如下:
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
//全局变量需要同步
synchronized (this.singletonObjects) {
//首先检查对应的bean是否已经加载过,因为singleton模式其实就是复用已创建的bean
Object singletonObject = this.singletonObjects.get(beanName);
//如果为空才可以进行singleton的bean的初始化
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 {
//初始化bean
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
catch (IllegalStateException ex) {
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) {
//加入代表已经初始化完成的单例bean缓存中
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
从上面可看到,当 Bean 实例初始化完成后将会删除 singletonFactories 和 earlySingletonObjects 缓存中的引用,添加到 singletonObjects 缓存中。
最后梳理一下Spring解决循环依赖的流程
1. 类 A 与 B 中属性相互引用,造成循环依赖
2. A 实例化完成,将自己提前曝光到 singletonFactories 缓存中
3. A 实例进行依赖注入,发现自己依赖对象 B,就尝试获取 B 实例引用
4. B 此时还没初始化,先进行实例化并将自己曝光到 singletonFactories 缓存中
5. B 实例进行依赖注入,发现自己依赖对象 A,就尝试获取 A 实例引用
6. 由于 A 实例尚未初始化完成,从 singletonObjects 中获取不到,从 earlySingletonObjects 中获取也没有,最后从 singletonFactories 中获取到
7. A 实例从 singletonFactories 中删除,添加到 earlySingletonObjects 缓存中去
8. B 实例拿到 A 实例引用后顺利完成依赖注入及初始化,并将自己从 singletonFactories 缓存中删除,添加到 singletonObjects 缓存中
9. A 实例获取 B 实例引用后,A 实例也能继续完成依赖注入及后续初始化操作
10. A 是从 earlySingletonObjects 中删除,添加到 singletonObjects 缓存中
在整个过程中,A 和 B 的实例引用并未改变过。只是在 Bean 生命周期的不同阶段。