默认情况下是允许Bean之间的循环依赖的,在依赖注入时Spring会尝试处理循环依赖。如果将该属性配置为“false”则关闭循环依赖,当在Bean依赖注入的时遇到循环依赖时抛出异常。可以通过如下方式关闭,但是一般都不这么做
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(“bean.xml”);
//禁用循环依赖
applicationContext.setAllowCircularReferences(false);
//刷新容器
applicationContext.refresh();
…
=======================================================================
构造器是不允许循环依赖的,动动你的小脑瓜想一想,比如:A 依赖 B ,B依赖C,C依赖A,在实例化A的时候,构造器需要注入B,然后Spirng会实例化B,此时的A属于“正在创建”的状态。当实例化B的时候,发现构造器需要注入C,然后去实例化C,然而实例化C的时候又需要注入A的实例,这样就造成了一个死循环,永远无法先实例化出某一个Bean,所以Spring遇到这里构造器循环依赖会直接抛出异常。
那么Spring到底是如何做的呢?
-
首先Spring会走Bean的实例化流程尝试创建 A 的实例 ,在创建实例之间先从 “正在创建Bean池” (一个缓存Map而已)中去查找A 是否正在创建,如果没找到,则将 A 放入 “正在创建Bean池”中,然后准备实例化构造器参数 B。
-
Spring会走Bean的实例化流程尝试创建 B 的实例 ,在创建实例之间先从 “正在创建Bean池” (一个缓存Map而已)中去查找B 是否正在创建,如果没找到,则将 B 放入 “正在创建Bean池”中,然后准备实例化构造器参数 A。
-
Spring会走Bean的实例化流程尝试创建 A 的实例 ,在创建实例之间先从 “正在创建Bean池” (一个缓存Map而已)中去查找A 是否正在创建。
-
此时:Spring发现 A 正处于“正在创建Bean池”,表示出现构造器循环依赖,抛出异常:“BeanCurrentlyInCreationException”
DefaultSingletonBeanRegistry#getSingleton
下面我们以 BeanA 构造参数依赖BeanB, BeanB 构造参数依赖BeanA 为例来分析。
当Spring的IOC容器启动,尝试对单利的BeanA进行初始化,根据之前的分析我们知道,单利Bean的创建入口是 AbstractBeanFactory#doGetBean
在该方法中会先从单利Bean缓存中获取
,如果没有代码会走到:DefaultSingletonBeanRegistry#getSingleton(jString beanName, ObjectFactory<?> singletonFactory)
方法中 ,在该方法中会先对把创建的Bean加入 一个名字为 singletonsCurrentlyInCreation 的 ConcurrentHashMap中,意思是该Bean正在创建中,
然后调用 ObjectFactory.getObject() 实例化Bean , 假设 BeanA 进入了该方法进行实例化:
//正在创建中的Bean
private final Set singletonsCurrentlyInCreation =
Collections.newSetFromMap(new ConcurrentHashMap<>(16));
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
…省略…
//把该Bean的名字加入 singletonsCurrentlyInCreation 正在创建池 中
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
//调用ObjectFactory创建Bean的实例
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
…省略…
//如果singletonsCurrentlyInCreation中没该Bean,就把该Bean存储到singletonsCurrentlyInCreation中,
//如果 singletonsCurrentlyInCreation 中有 该Bean,就报错循环依赖异常BeanCurrentlyInCreationException
//也就意味着同一个beanName进入该方法2次就会抛异常
protected void beforeSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
beforeSingletonCreation 方法非常关键 ,它会把beanName加入 singletonsCurrentlyInCreation,一个代表“正在创建中的Bean”的ConcurrentHashMap中。
-
如果singletonsCurrentlyInCreation中没该beanName,就把该Bean存储到singletonsCurrentlyInCreation中,
-
如果 singletonsCurrentlyInCreation 中有 该Bean,就报错循环依赖异常BeanCurrentlyInCreationException
【注意】也就意味着同一个beanName进入该方法2次就会抛异常 , 现在BeanA已经加入了singletonsCurrentlyInCreation
AbstractAutowireCapableBeanFactory#autowireConstructor
我们前面分析过 ObjectFactory.getObject实例化Bean的详细流程,这里我只是大概在复盘一下就行了。因为我们的BeanA的构造器注入了一个BeanB,所以 代码最终会走到AbstractAutowireCapableBeanFactory#autowireConstructor
,通过构造器来实例化BeanA(在属性注入那一章有讲到 ) 。
在autowireConstructor 方法中会通过 ConstructorResolver#resolveConstructorArguments
来解析构造参数,调用 BeanDefinitionValueResolver 去把 ref="beanB"
这种字符串的引用变成一个实实在在的Bean,即BeanB,所以在 BeanDefinitionValueResolver
属性值解析器中又会去实例化BeanB,同样会走到 DefaultSingletonBeanRegistry#getSingleton
中把BeanB加入 singletonsCurrentlyInCreation
“正在创建Bean池”中,然后调用ObjectFactory.getObject
实例化BeanB。
低于BeanB而已同样需要通过构造器创建,BeanB构造器参数依赖了BeanA,也就意味着又会调用 BeanDefinitionValueResolver 去把 ref=“beanA” 这种字符串引用变成容器中的BeanA的Bean实例,然后代码又会走到 DefaultSingletonBeanRegistry#getSingleton。然后再一次的尝试把BeanA加入singletonsCurrentlyInCreation
“正在创建Bean池”。
此时问题就来了,在最开始创建BeanA的时候它已经加入过一次“正在创建Bean” 池,这会儿实例化BeanB的时候,由于构造器参数依赖了BeanA,导致BeanA又想进入“正在创建Bean” 池 ,此时 Spring抛出循环依赖异常:
Error creating bean with name ‘beanA’: Requested bean is currently in creation: Is there an unresolvable circular reference?
到这,Spring处理构造器循环依赖的源码分析完毕。
==========================================================================
setter循环依赖是可以允许的。Spring是通过提前暴露未实例化完成的Bean的 ObjectFactory 来实现循环依赖的,这样做的目的是其他的Bean可以通过 ObjectFactory 引用到该Bean。
实现流程如下:
-
Spring创建BeanA,通过无参构造实例化,把BeanA添加到“正在创建Bean池”中,并暴露当前实例的ObjectFactory,即把ObjectFactory添加到singletonFactories(三级缓存)中,该ObjectFactory用来获取创建中的BeanA,然后,然后通过setter注入BeanB
-
Spring创建BeanB,通过无参构造实例化,把BeanB添加到“正在创建Bean池”中,并暴露一个ObjectFactory,然后,然后通过setter注入BeanA
-
在BeanB通过setter注入BeanA时,由于BeanA 提前暴露了ObjectFactory ,通过它返回一个提前暴露一个创建中的BeanA。
-
然后完成BeanB的依赖注入
这里补张图:
获取Bean的时候走三级缓存
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//一级缓存,存储实例化好的Bean
Object singletonObject = this.singletonObjects.get(beanName);
//如果单利缓存池中没有,但是beanName正在创建
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
//获取二级缓存,这个里面存储的是正在创建的Bean,半成品
singletonObject = this.earlySingletonObjects.get(beanName);
//如果也为空,但是允许循环依赖
if (singletonObject == null && allowEarlyReference) {
//从三级缓存获取Bean的创建工厂,
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
//创建Bean的实例
singletonObject = singletonFactory.getObject();
//把Bean存储到二级缓存
this.earlySingletonObjects.put(beanName, singletonObject);
//移除三级缓存中的创建工厂
this.singletonFactories.remove(beanName);
}
}
}
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
AbstractAutowireCapableBeanFactory#doCreateBean
我们以BeanA 通过settter依赖BeanB,BeanB通过setter 依赖BeanA为例来分析一下源码,在之前的Bean实例化流程分析过程中我们了解到,Bean的实例化会走AbstractBeanFactory#doGetBean
,然后查找单利缓存中是否有该Bean ,如果没有就调用 DefaultSingletonBeanRegistry#getSingleton
,方法会把BeanA加入 singletonsCurrentlyInCreation
“创建中的Bean池”,然后调用ObjectFactory.getObject创建Bean.
org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
源码:
protected T doGetBean(final String name, @Nullable final Class requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
final String beanName = transformedBeanName(name);
Object bean;
// Eagerly check singleton cache for manually registered singletons.
//缓存中获取Bean,解决了循环依赖问题
Object sharedInstance = getSingleton(beanName);
…缓存中没有走下面…
if (mbd.isSingleton()) {
//走 DefaultSingletonBeanRegistry#getSingleton ,方法会把bean加入“正在创建bean池”
//然后调用ObjectFactory实例化Bean
sharedInstance = 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;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
第一次进来,缓存中是没有BeanA的,所有会走 getSingleton 方法,然后代码最终会走到AbstractAutowireCapableBeanFactory#doCreateBean
方法中 。
AbstractAutowireCapableBeanFactory#doCreateBean源码:
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
//实例化Bean
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
…省略…
//如果是单利 ,如果是允许循环依赖,如果 beanName 出于创建中,已经被添加到“创建中的bean池”
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isDebugEnabled()) {
logger.debug(“Eagerly caching bean '” + beanName +
“’ to allow for resolving potential circular references”);
}
//把ObjectFactory 添加到 singletonFactories 中。
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
try {
//走依赖注入流程
populateBean(beanName, mbd, instanceWrapper);
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
//缓存单利Bean的创建工厂,用于解决循环依赖
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, “Singleton factory must not be null”);
synchronized (this.singletonObjects) {
//singletonObjects单利缓存中是否包含Bean
if (!this.singletonObjects.containsKey(beanName)) {
//提前暴露ObjectFactory,把ObjectFactory放到singletonFactories中,
//后面解决循环依赖,获取Bean实例的时候会用到
this.singletonFactories.put(beanName, singletonFactory);
//早期单利bean缓存中移除Bean
this.earlySingletonObjects.remove(beanName);
//把注册的Bean加入registeredSingletons中
this.registeredSingletons.add(beanName);
}
}
}
该方法中把BeanA实例化好之后,会把ObjectFactory存储到一个 singletonFactories (HashMap)中来提前暴露Bean的创建工厂,用于解决循环依赖
【重要】,然后调用 populateBean 走属性注入流程。
属性注入会通过BeanDefinition得到bean的依赖属性,然后调用 AbstractAutowireCapableBeanFactory#applyPropertyValues ,把属性应用到对象上。在applyPropertyValues 方法中最终调用 BeanDefinitionValueResolver#resolveValueIfNecessary 解析属性值,比如:ref=“beanB” 这种字符串引用变成 对象实例的引用。
在BeanDefinitionValueResolver解析依赖的属性值即:BeanB的时候,同样会触发BeanB的实例化,代码会走到AbstractBeanFactory#doGetBean
,然后走方法 DefaultSingletonBeanRegistry#getSingleton
中把BeanB加入 singletonsCurrentlyInCreation
“创建中的Bean池”,然后代码会走到AbstractAutowireCapableBeanFactory#doCreateBean
方法中创建BeanB,
该方法中会先实例化BeanB,接着会把BeanB的ObjectFactory存储到 singletonFactories (HashMap)中来提前暴露Bean的创建工厂,用于解决循环依赖
,然后调用 populateBean 走属性注入流程。
同样因为BeanB通过Setter 注入了 A,所以在 populateBean 属性注入流程中会解析 ref=“beanA” 为容器中的 BeanA 的实例。
然后会走到 AbstractBeanFactory#doGetBean 中获取BeanA的实例。这个时候流程就不一样了,我们先看一下 AbstractBeanFactory#doGetBean 中的代码
protected T doGetBean(final String name, @Nullable final Class requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
final String beanName = transformedBeanName(name);
Object bean;
// Eagerly check singleton cache for manually registered singletons.
//从缓存中获取Bean
Object sharedInstance = getSingleton(beanName);
…省略…
//如果缓存中没有Bean,就创建Bean
if (mbd.isSingleton()) {
sharedInstance = 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;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
在获取单利Bean的实例的时候是会先去单利Bean的缓存中去查看Bean是否已经存在
,如果不存在,才会走DefaultSingletonBeanRegistry#getSingleton方法创建Bean。
问题是:此刻单利Bean缓存中已经有BeanA了,因为在最开始BeanA已经出于“正在创建Bean池”中了。我们先来看一下是如何从缓存获取Bean的。
DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)
源码如下:
//allowEarlyReference :是否创建早期应用,主要用来解决循环依赖
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// Quick check for existing instance without full singleton lock
//从Map中 singletonObjects = new ConcurrentHashMap<>(256); 获取单利Bean
//【一级缓存】singletonObject缓存中是否有Bean , 它存储的是已经实例化好的Bean
Object singletonObject = this.singletonObjects.get(beanName);
//如果singletonObjects中没有Bean,但是Bean出于正在创建池中,即: Set singletonsCurrentlyInCreation中有Bean,
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
//【二级缓存】从早期单例对象的缓存 earlySingletonObjects 中获取
singletonObject = this.earlySingletonObjects.get(beanName);
//早期单利对象缓存中也没有,但是允许循环依赖
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
// Consistent creation of early reference within full singleton lock
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
//【三级缓存】获取ObjectFactory , 对象创建工厂,得到Bean创建过程中提前暴露的工厂。
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
//通过工厂ObjectFactory 获取对象实例
singletonObject = singletonFactory.getObject();
//把对象存储到早期缓存中
this.earlySingletonObjects.put(beanName, singletonObject);
//把ObjectFactory移除
this.singletonFactories.remove(beanName);
最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
tonFactories.remove(beanName);
最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-yQIAs1Yl-1715092458342)]
[外链图片转存中…(img-jcOrjPsq-1715092458343)]
[外链图片转存中…(img-JPma2HP4-1715092458343)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!