IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Spring中构造方法中产生循环依赖注入失败问题 -> 正文阅读

[Java知识库]Spring中构造方法中产生循环依赖注入失败问题

一、简单聊一聊bean的生命周期

1.1、名词讲解

springfactory中的三级缓存

singletonObjects【一级】、earlySingletonObjects【二级】、singletonFactories【三级】

三级缓存分别作用:

一级:存储经历过完整bean的生命周期后的对象

二级:在属性注入情况下存在循环依赖情况,保证对象的单例

三级:打破循环依赖,其内部存储为bean名称+普通对象

1.2、案例分析

假设有三个类A、B、C

生命A对象生命周期

1、根据构造方法反射生成未初始化对象A —>并将生成的对象存储到三级缓存singletonFactories中

2、属性填充B(B不存在对A的属性依赖)

3、属性填充完毕后执行:aware相关接口、初始化前、初始化、初始化后(可能执行aop生成代理对象)

4、将执行完生命周期后的bean存放到singletonObjects中

1.3、核心代码分析

//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) {
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
		final Object bean = instanceWrapper.getWrappedInstance();
		Class<?> beanType = instanceWrapper.getWrappedClass();
		if (beanType != NullBean.class) {
			mbd.resolvedTargetType = beanType;
		}

		//。。。。
		//作为判断依据为存放三级缓存
		boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
				isSingletonCurrentlyInCreation(beanName));
		if (earlySingletonExposure) {
			//将未初始化的bean对象存放到三级缓存中,存储的未beanName + lambda表达式
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}

         //最终生成对象bean
		Object exposedObject = bean;
		try {
			//对象属性填充
			populateBean(beanName, mbd, instanceWrapper);
			//属性填充完毕后执行:aware相关接口、初始化前、初始化、初始化后(可能执行aop生成代理对象)
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}
		catch (Throwable ex) {
			//。。。。
		}

    	//正常情况下earlySingletonExposure==true
		if (earlySingletonExposure) {
			Object earlySingletonReference = getSingleton(beanName, false);
			if (earlySingletonReference != null) {
                  //相等说明未进行aop操作
				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()) {
						//。。。。
				}
			}
		}

		// 主要包含注册 destroy method
		try {
			registerDisposableBeanIfNecessary(beanName, bean, mbd);
		}
		catch (BeanDefinitionValidationException ex) {
			//。。。
		}

    	//返回最终对象bean,两种情况:aop之后的代理bean、普通bean
		return exposedObject;
	}

二、为什么会存在构造方法注入属性时存在循环依赖报错

@Component
class A {
    private B b;
    private C c;
    
    public A(B b,C c){...}
}
@Component
class B {
    @Autowired
    private A a;
}
@Component
class C {
    @Autowired
    private A a;
}

案例分析:

1、初始化生成A对象时发现内部仅有一个有参构造方法,故选择其进行class反射,故去ioc容器中寻找B、C的对象进行属性注入。但请注意在该过程中还未生成A的初始化前对象,也就是说初始化前的对象A还未存储对第三级缓存中,故此时在AbstractBeanFactory.doGetBean方法会执行else方法体,即创建新的对象。

2、在ioc容器中寻找B、C时发现其还未创建。故通过空构造方法进行构造对象b、c。紧接着进行属性注入A,但在ioc容器中发现其不存在,故又进入创建A对象,但此时发现A对象正在创建中,从而报BeanCurrentlyInCreationException异常。


//DefaultSingletonBeanRegistry.getSingleton
/**
	执行流程:
		1.从一级缓存singletonObjects中获取对象
		2.若存在则直接返回,不存在则首先执行beforeSingletonCreation方法将当前创建bean对象记录
		到singletonsCurrentlyInCreation中,若不存在循环依赖则执行singletonFactory.getObject();
		进行创建bean对象并返回结果
		3.但存在循环依赖情况下会进行
		if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
			throw new BeanCurrentlyInCreationException(beanName);
		}方法中的!this.singletonsCurrentlyInCreation.add(beanName)为true进而抛出异常
*/
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
		synchronized (this.singletonObjects) {
			Object singletonObject = this.singletonObjects.get(beanName);
			if (singletonObject == null) {
				//....
				beforeSingletonCreation(beanName);
				boolean newSingleton = false;
				boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
				if (recordSuppressedExceptions) {
					this.suppressedExceptions = new LinkedHashSet<>();
				}
				try {
					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;
		}
//DefaultSingletonBeanRegistry.beforeSingletonCreation
protected void beforeSingletonCreation(String beanName) {
    if (!this.inCreationCheckExclusions.contains(beanName) && 	!this.singletonsCurrentlyInCreation.add(beanName)) {
       throw new BeanCurrentlyInCreationException(beanName);
    }
}

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

结论

构造方法中存在循环依赖时由于不能在三级缓存中获取普通对象,从而导致循环创建对象报BeanCurrentlyInCreationException异常。

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-04-24 09:15:04  更:2022-04-24 09:19:06 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 2:53:28-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码