当前位置: 首页 > article >正文

Spring系列 循环依赖

文章目录

  • 注入方式
  • 循环依赖的场景
  • 单例创建流程
    • getSingleton
    • createBean
      • doCreateBean
      • createBeanInstance
  • 循环依赖分析
    • 为什么都使用构造函数无法解决?
    • 为什么使用@Autowired可以解决?
    • 为什么要添加到 earlySingletonObjects 缓存中?
    • allowCircularReferences 的作用
    • 只用二级缓存能解决循环依赖吗?
    • 二级缓存什么时候放入一级缓存?
    • 循环依赖的暴露顺序
  • 参考资料

注入方式

一般注入方式可以分为自动注入和手动注入
自动注入我分为以下几类

  1. 构造函数参数
  2. setter 方法 + @Autowire,字段 + @Autowire/@Resource

除了自动注入,在 Bean 的生命周期回调中通过 ApplicationContext 进行手动赋值,我将这种方式称为手动注入

循环依赖的场景

下面是不同场景下循环依赖的解决情况的测试结果

依赖情况依赖注入方式循环依赖是否被解决
AB相互依赖均采用setter方法注入
AB相互依赖
allowCircularReferences = false
均采用setter方法注入
AB相互依赖均采用构造器注入
AB相互依赖A中注入B的方式为setter方法,
B中注入A的方式为构造器
AB相互依赖
allowCircularReferences = false
A中注入B的方式为setter方法,
B中注入A的方式为构造器
AB相互依赖B中注入A的方式为setter方法,
A中注入B的方式为构造器

单例创建流程

getSingleton

首先调用 getBean 时会先到单例缓存中去获取一次
在这里插入图片描述

getSingleton(beanName, true)这个方法实际上就是到缓存中尝试去获取Bean,在org.springframework.beans.factory.support.DefaultSingletonBeanRegistry这个类中有三个Map,对应的就是这些缓存

  1. singletonObjects,存储的是所有创建好了的单例Bean
  2. earlySingletonObjects,完成实例化,但是还未进行属性注入及初始化的对象
  3. singletonFactories,提前暴露的一个单例工厂

如下图所示:

在这里插入图片描述

下面我简化了一下 doGetBean 的部分,保留核心流程

protected <T> T doGetBean(
		String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
		throws BeansException {
	// 最终要返回的对象
	Object beanInstance;
	// Eagerly check singleton cache for manually registered singletons.
	Object sharedInstance = getSingleton(beanName);
	if (sharedInstance != null && args == null) {
		// ... 此处简化了部分代码
		beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
	} else {
		// ... 此处简化了部分代码
		if (mbd.isSingleton()) {
			sharedInstance = getSingleton(beanName, () -> {
				try {
					return createBean(beanName, mbd, args);
				} catch (BeansException ex) {
					// ... 此处简化了部分代码
					destroySingleton(beanName);
					throw ex;
				}
			});
			beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
		}
		// ... 此处简化了部分代码
	}
	return beanInstance;
}

当获取一个 Bean 时会先去缓存中查一次,如果没有则走 getSingleton 带 ObjectFactory 参数的实现

Object getSingleton(String beanName, ObjectFactory<?> singletonFactory)

这里传入的 ObjectFactory 是一个 lambda 表达式,就简单调用了 createBean 方法并返回,这个方法就是用来创建Bean的。

同样,简化后的方法内容如下:

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
	
	synchronized (this.singletonObjects) {
		Object singletonObject = this.singletonObjects.get(beanName);
		if (singletonObject == null) {
			// ... 此处省略部分代码
			beforeSingletonCreation(beanName);
			boolean newSingleton = false;
			// ... 此处省略部分代码
			try {
				// 调用 createBean 
				singletonObject = singletonFactory.getObject();
				newSingleton = true;
			} catch (IllegalStateException ex) {
				// ... 此处省略部分代码
			} finally {
				// ... 此处省略部分代码
				afterSingletonCreation(beanName);
			}
			if (newSingleton) {
				addSingleton(beanName, singletonObject);
			}
		}
		return singletonObject;
	}
}

调用 createBean 的前后分别调用了 beforeSingletonCreationafterSingletonCreation,这两个方法分别用于标记一个 beanName 是否在创建中以及清除标记

protected void beforeSingletonCreation(String beanName) {
	if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
		throw new BeanCurrentlyInCreationException(beanName);
	}
}

protected void afterSingletonCreation(String beanName) {
	if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
		throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
	}
}

最后调用了 addSingleton 方法,此方法的内容就是将 createBean 创建的对象放入 singletonObjects 这个缓存中,并且从 singletonFactories 和 earlySingletonObjects 这两个缓存中移除当前创建的这个 beanName

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);
	}
}

从这我们就知道一个获取单例的一个流程:

  1. 先从单例池中获取一次,如果没有,则进行创建
  2. 创建前,标记当前 beanName 为创建中
  3. 创建 beanName 对应的对象
  4. 创建后,清除标记
  5. 将创建的对象添加到 signletonObjects 缓存中

在这里插入图片描述

createBean

createBean 这个方法是在 AbstractBeanFactory 的子类 AbstractAutowireCapableBeanFactory 中实现的

protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
		throws BeanCreationException {
	try {
		// 可以通过 BeanPostProcessors 返回一个代理对象
		Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
		if (bean != null) {
			return bean;
		}
	} catch (Throwable ex) {
		// ... 省略部分代码
	}
	try {
		Object beanInstance = doCreateBean(beanName, mbdToUse, args);
		// ... 省略部分日志代码
		return beanInstance;
	} catch (XxxException... ex) {
		// ... 省略部分异常处理代码
		throw ex;
	}
}

resolveBeforeInstantiation 这部分先不看,这是关于代理对象的逻辑,doCreateBean 是实际创建对象的逻辑。现在我们只需要知道如果 resolveBeforeInstantiation 方法返回了一个对象,那么 createBean 方法根本不会走 doCreateBean ,也就是不回去创建当前这个 beanName 的对象。

doCreateBean

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
	// 实例化 Bean
	BeanWrapper instanceWrapper = null;
	if (mbd.isSingleton()) {
		// 先从 factoryBeanInstanceCache 这个缓存中获取
		instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
	}
	if (instanceWrapper == null) {
		// 创建对象
		instanceWrapper = createBeanInstance(beanName, mbd, args);
	}
	Object bean = instanceWrapper.getWrappedInstance();
	Class<?> beanType = instanceWrapper.getWrappedClass();
	if (beanType != NullBean.class) {
		mbd.resolvedTargetType = beanType;
	}

	// 执行 MergedBeanDefinitionPostProcessor
	synchronized (mbd.postProcessingLock) {
		if (!mbd.postProcessed) {
			applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
			// ...
			mbd.postProcessed = true;
		}
	}

	// Eagerly cache singletons to be able to resolve circular references
	// even when triggered by lifecycle interfaces like BeanFactoryAware.
	boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
			isSingletonCurrentlyInCreation(beanName));
	if (earlySingletonExposure) {
		// ... 省略部分日志记录代码
		addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
	}

	// Initialize the bean instance.
	Object exposedObject = bean;
	try {
		populateBean(beanName, mbd, instanceWrapper);
		exposedObject = initializeBean(beanName, exposedObject, mbd);
	} catch (Throwable 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
				}
			}
		}
	}

	// Register bean as disposable.
	try {
		registerDisposableBeanIfNecessary(beanName, bean, mbd);
	} catch (BeanDefinitionValidationException ex) {
		// ...
	}
	return exposedObject;
}

在这里插入图片描述

addSingletonFactory 的目的就是将 ObjectFactory 添加到 singletonFactories 这个缓存中

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
	// ...
	synchronized (this.singletonObjects) {
		if (!this.singletonObjects.containsKey(beanName)) {
			this.singletonFactories.put(beanName, singletonFactory);
			this.earlySingletonObjects.remove(beanName);
			this.registeredSingletons.add(beanName);
		}
	}
}

执行 addSingletonFactory 之前有个前提条件,就是 (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));
前面已经说过,createBean 之前,会标记当前 beanName 在创建中,所以 isSingletonCurrentlyInCreation(beanName)); 是为 true 的,
在这里插入图片描述

getEarlyBeanReference 这个方法是调用 SmartInstantiationAwareBeanPostProcessor 这个扩展点的,它是在刚创建好像时进行调用,此时还未执行 populateBean 操作。

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

createBeanInstance

这里要重点了解一下 createBeanInstance 方法,这个方法是创建一个对象的逻辑

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
	// Make sure bean class is actually resolved at this point.
	Class<?> beanClass = resolveBeanClass(mbd, beanName);
	// ...
	Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
	if (instanceSupplier != null) {
		return obtainFromSupplier(instanceSupplier, beanName);
	}

	if (mbd.getFactoryMethodName() != null) {
		return instantiateUsingFactoryMethod(beanName, mbd, args);
	}
	// Shortcut when re-creating the same bean...
	boolean resolved = false;
	boolean autowireNecessary = false;
	if (args == null) {
		synchronized (mbd.constructorArgumentLock) {
			if (mbd.resolvedConstructorOrFactoryMethod != null) {
				resolved = true;
				autowireNecessary = mbd.constructorArgumentsResolved;
			}
		}
	}
	if (resolved) {
		if (autowireNecessary) {
			return autowireConstructor(beanName, mbd, null, null);
		} else {
			return instantiateBean(beanName, mbd);
		}
	}

	// Candidate constructors for autowiring?
	Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
	if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
			mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
		return autowireConstructor(beanName, mbd, ctors, args);
	}
	// Preferred constructors for default construction?
	ctors = mbd.getPreferredConstructors();
	if (ctors != null) {
		return autowireConstructor(beanName, mbd, ctors, null);
	}
	// No special handling: simply use no-arg constructor.
	return instantiateBean(beanName, mbd);
}

循环依赖分析

为什么都使用构造函数无法解决?

因为创建 A 时, createBeanInstance 里如果使用构造函数进行对象的创建,那么会进行依赖注入,如果发现构造函数中依赖了 B,那么会通过 getBean 方法去获取 B 这个对象,因此会再次走创建单例的逻辑,于是又会碰到 B 的构造方法依赖 A 的情况,发生了循环依赖,因此会抛出下面的异常信息:Error creating bean with name ‘xxx’: Requested bean is currently in creation: Is there an unresolvable circular reference?

这个异常就是在 beforeSingletonCreation 方法抛出的
在这里插入图片描述

整个过程如下:

  1. 第一次标记了 A,还未清除 A,就创建 B
  2. 于是标记 B ,由于B依赖A,所以会再次标记 A 在创建中
  3. 由于 A 已被标记,所以直接抛异常

其实本质原因就是构造函数必须参数要有值才能创建这个对象

为什么使用@Autowired可以解决?

使用 @Autowired 时属性注入其实不是在 populateBean 方法中进行的,而是在 initializeBean 的 BeanPostProcessor 中进行的。
没有构造函数依赖的情况下,会默认通过无参构造创建,先创建这个对象,然后进行相关依赖属性的填充。所以区别就在于使用@Autowire可以创建对象,虽然此时这个对象还未进行属性填充,但是使用构造函数依赖连对象都不能创建。

因为 B 需要自动注入 A,所以在创建B的时候,又会去调用getBean(a),这个时候就又回到之前的流程了

在这里插入图片描述
但是不同的是,因为之前A在实例化后已经将其放入了三级缓存singletonFactories中,所以此时getBean(A)可以直接从缓存中获取

在这里插入图片描述

为什么要添加到 earlySingletonObjects 缓存中?

个人认为是为了性能考虑。举个例子:目前是只有 A 和 B 之间存在循环依赖,但是如果存在多层循环依赖的的话,如下所示。那么执行 doGetBean 方法时直接从而及缓存中就可以获取到,直接就返回了,就没有后续的那么多操作了

class A {
    B b;
}

class B {
    A a;
    C c;
}

class C {
    A a;
    B b;
}

allowCircularReferences 的作用

allowCircularReferences,当出现循环依赖时是否自动尝试解决,默认为 true。

在这里插入图片描述

直观点儿的解释就是控制是否将创建的 bean 加入 singletonFactories 缓存

在这里插入图片描述

如果是普通的 spring 项目,可以以下面的方式进行设置

@ComponentScan
public class Main {
    public static void main(String[] args) {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        beanFactory.setAllowCircularReferences(false);
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(beanFactory);
        context.register(Main.class);
        context.refresh();
    }
}

如果是springboot环境下则需要配置spring.main.allow-circular-references=true

allowCircularReferences 为 false 的情况下即使都用@Autowire注入还是会报错。例如默认情况下 springboot 项目就会报错,因为 allowCircularReferences 这个配置默认是 false

Description:

The dependencies of some of the beans in the application context form a cycle:

┌─────┐
|  a
↑     ↓
|  b (field org.example.springboot.config.A org.example.springboot.config.B.a)
└─────┘

Action:

Relying upon circular references is discouraged and they are prohibited by 
default. Update your application to remove the dependency cycle between 
beans. As a last resort, it may be possible to break the cycle automatically 
by setting spring.main.allow-circular-references to true.

因为 allowCircularReferences 是 false

在这里插入图片描述

allowCircularReferences 是 false 的情况下是根本不会调用 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); 这句代码的。也就是

在这里插入图片描述
所以这里是一定要求 singletonObjects 或者 earlySingletonObjects 是有所需的对象的
在这里插入图片描述

只用二级缓存能解决循环依赖吗?

能解决非构造函数循环依赖的根本条件是:A 依赖 B 时,要去创建 B ,此时 A 已经创建。我们需要将 A 或者获取 A 的逻辑保存在一个位置,后面 B 依赖 A 时能够直接获取到。只使用一个缓存也可以实现这个目的。那为什么要设计这 3 个缓存呢?

个人理解:Spring 容器本质就是一个Map,其key为beanName,value为对象。而我们通过BeanFactory#getBean获取的Bean 需要是可以使用的。由于循环依赖的存在,一个对象的依赖是需要在该对象之前先创建好的,所以这个时候不可避免的出现了这种虽然实例化了,但是属性还未填充的Bean,这种半成品Bean也是不可使用的。

如果一个不可使用的 Bean 还未创建好,就通过 getBean 去获取,就可能会出错。并且循环依赖场景如果想要靠 Spring 自身解决循环依赖是一定会存在需要早期暴露的对象的。比如 A 依赖 B,那么创建 B 时不可能拿到已经创建好的 A 对象,这两种对象一定要分开存放。所以这个时候需要两个 Map 来存放,一个 singletonObjects 放已经创建好的,另外一个 earlySingletonObjects 放需要早期暴露的对象。

那么为什么需要 singletonFactories 这个ObjectFactory工厂的缓存呢?

个人认为和 AOP 应该没有关系,AOP 只是利用这几个缓存实现,而非为了 实现 AOP 才使用 singletonFactories 这个缓存。使用 singletonFactories 这个就是为了不在对象未完全创建时进行暴露。

因为创建 Bean 之后紧接着又要调用其他 API ,比如填充属性 populateBean、初始化 initializeBean 等,在这些 API 中可以通过 ApplicationContextAware、BeanFactoryAware 等方式拿到 BeanFactory 并调用 getBean 方法。ObjectFactory 的作用只是将这个对象暴露的时间延迟到属性填充和初始化完毕之后了。直到 ObjectFactory 调用之前, earlySingletonObjects 都是没有值的,而 ObjectFactory 只会调用一次,调用后就直接删除了。

在这里插入图片描述

举个例子,现在 A 与 B、C 分别形成循环依赖。当 A 对象创建好,但是还未填充属性时,这个时候发现 A 依赖 B,于是创建 B。这个时候 A 应该放在哪呢?首先singletonObjects 肯定是不能放的,那么是放 earlySingletonObjects 吗?也不行,因为 getBean 只能从某一个地方取,不管从哪儿取拿到的都是不能用的早期对象。所以这个时候不能直接放对象,而是放一个 ObjectFactory,此时该对象还没暴露出去的,不能通过 API 拿到。真正想要获取的时候再通过 ObjectFactory 进行获取。为什么是从 singletonFactories 中获取放入 earlySingletonObjects 呢?因为现在不只 B 需要 A,C 也需要 A,但是 B 获取 A 时是不知道 C 的存在的,此时 A 仍是早期对象。

二级缓存什么时候放入一级缓存?

放入一级缓存只有一个方式,调用 addSingleton 方法,这个是内部 API
在这里插入图片描述

而调用此方法只有 2 个地方

  1. 一是Object getSingleton(String beanName, ObjectFactory<?> singletonFactory)在依赖注入完成时

在这里插入图片描述

  1. 通过SingletonBeanRegistry#registerSingleton这个public API手动注册单例对象

在这里插入图片描述

依赖注入完成时放入

存在循环依赖的情况下 getBean 是一个递归的过程。递归开始的地方有 2 个,都在 doCreateBean 方法里,一个是根据构造函数创建对象时,

在这里插入图片描述

另外一个是对象创建好之后进行初始化的时候

在这里插入图片描述
在 createBean 方法里,如果允许暴露早期对象,会自动调用 getSingleton ,此时 allowEarlyReference 为 false,因为此时 earlySingletonObjects 这个缓存里已经有了(退出递归的结果就是报错或者完成依赖注入被放入 earlySingletonObjects 缓存)
在这里插入图片描述

这个调用的目的就是将 earlySingletonObjects 缓存再放入 singletonObjects 缓存

Object earlySingletonReference = getSingleton(beanName, false);

如下图所示

在这里插入图片描述

循环依赖的暴露顺序

A 依赖 B ,B 先暴露,最后才是 A,因为是个递归过程

参考资料

  1. https://developer.aliyun.com/article/766880

http://www.kler.cn/news/341561.html

相关文章:

  • 【必看!!!】Python—requests模块详解!(文末附带无偿大礼包)
  • Patroni配置文件1-动态配置
  • 使用Python实现系统时间跳变检测与日志记录
  • 【斯坦福CS144】Lab7
  • 基于floor函数报错注入sqli-labs less-5和less-6
  • Ajax面试题:(第二天)
  • 毕设分享 基于协同过滤的电影推荐系统
  • 自动化测试框架(全)
  • 2024双十一究竟买什么比较好?为您精选五款双十一必购好物清单!
  • 汽车主机厂主数据管理中一物多码或多码一物问题的具体表现有哪些?
  • 游戏盾是如何解决游戏行业攻击问题
  • LangChain使用few shot template
  • RK3568驱动指南|第十六篇 SPI-第190章 配置模式下寄存器的配置
  • 《Spring Microservices in Action, 2nd Edition》读后总结
  • vue 权限分组
  • 实景三维赋能矿山安全风险监测预警
  • “万万没想到”,“人工智能”获得2024年诺贝尔物理学奖
  • 比较三组迭代次数的变化
  • ElasticSearch之网络配置
  • 独享动态IP是什么?它有什么独特优势吗?