spring实例化bean之循环依赖

news2024/11/23 2:12:15

serviceA里注入了serviceB,serviceB里又注入了serviceA,这样在实例化serviceA时,在doCreateBean时的populateBean时会先实例化serviceB,然后实例化serviceB,在serviceB的doCreateBean方法的populateBean又会去找servcieA,这样循环依赖就产生了。
解决办法就是在populateBean之前把当前的serviceA提前暴露,然后在实例化B时可以找到这个提前暴露的serviceA。
关键方法是doCreateBean方法中的addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

1. DefaultSingletonBeanRegistry#getSingleton

这里就是对当前bean做了一个标志,在实例化前表明当前bean在创建中,在实例化后删除这个标志

	public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
		Assert.notNull(beanName, "Bean name must not be null");
		synchronized (this.singletonObjects) {
			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 + "'");
				}
				//关键1 标志A这个Bean在创建中
				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;
					}
					//关键2 //this.singletonsCurrentlyInCreation.remove(beanName)
					//删除掉Bean A创建中的标识
					afterSingletonCreation(beanName);
				}
				if (newSingleton) {
					addSingleton(beanName, singletonObject);
				}
			}
			return singletonObject;
		}
	}

2. 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;
		}

		// Allow post-processors to modify the merged bean definition.
		synchronized (mbd.postProcessingLock) {
			if (!mbd.postProcessed) {
				try {
					applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
				}
				catch (Throwable ex) {
					throw new BeanCreationException(mbd.getResourceDescription(), beanName,
							"Post-processing of merged bean definition failed", ex);
				}
				mbd.postProcessed = true;
			}
		}

		// Eagerly cache singletons to be able to resolve circular references
		// even when triggered by lifecycle interfaces like BeanFactoryAware.
		//这里如果是单例 并且允许循环依赖 并且是正在创建的bean(1中设置)
		//能满足这个三个条件,则Spring会认为这个此时A是需要提前暴漏的单例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));
		}

		// Initialize the bean instance.
		Object exposedObject = bean;
		try {
			populateBean(beanName, mbd, instanceWrapper);
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}
		catch (Throwable ex) {
			if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
				throw (BeanCreationException) ex;
			}
			else {
				throw new BeanCreationException(
						mbd.getResourceDescription(), beanName, "Initialization of bean failed", 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 new BeanCurrentlyInCreationException(beanName,
								"Bean with name '" + beanName + "' has been injected into other beans [" +
								StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
								"] in its raw version as part of a circular reference, but has eventually been " +
								"wrapped. This means that said other beans do not use the final version of the " +
								"bean. This is often the result of over-eager type matching - consider using " +
								"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
					}
				}
			}
		}

		// Register bean as disposable.
		try {
			registerDisposableBeanIfNecessary(beanName, bean, mbd);
		}
		catch (BeanDefinitionValidationException ex) {
			throw new BeanCreationException(
					mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
		}

		return exposedObject;
	}

3. DefaultSingletonBeanRegistry#addSingletonFactory

实例化A时,doCreateBean方法populateBean方法执行之前,也就是实例化注入的serviceB之前会调用这个方法。
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

	protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
		Assert.notNull(singletonFactory, "Singleton factory must not be null");
		synchronized (this.singletonObjects) {
			if (!this.singletonObjects.containsKey(beanName)) {
				//把当前bean 也就是serviceA关联一个singletonFactory(也就是那个lambda表达式)
				this.singletonFactories.put(beanName, singletonFactory);
				this.earlySingletonObjects.remove(beanName);
				this.registeredSingletons.add(beanName);
			}
		}
	}

给A关联了一个ObjectFactory,也就是getEarlyBeanReference对应的lambda函数对象。放入了singletonFactories中,当B去查BeanA的时候就可以通过getEarlyBeanReference方法获取exposedObject这个早期bean对象。

4.AbstractAutowireCapableBeanFactory#getEarlyBeanReference

这里基本就是把传入的bean返回。命名为exposedObject 早期暴露对象
调用逻辑就是 serviceB实例化时需要注入serviceA,然后在doGetBean的最开始调用getSingleton方法时会获取到serviceA的ObjectFactory,然后执行singletonFactory.getObject()就会执行这个方法拿到bean。

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

查找
比如在实例化serviceA时需要实例化serviceB,实例化serviceB时populateBean方法会再尝试实例化serviceA,doGetBean方法开始会调用getSingleton方法,这个方法里会找到提前暴露的serviceA,保证serviceB可以顺利创建。

	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		//这里获取不到 因为servcieA的实例化还没完成 也就是servcieA的createBean方法还没执行完,servcieA的getSingleton方法的
		//singletonObject = singletonFactory.getObject()方法也就没执行完,下面的addSingleton方法
		//就不会执行(在这里执行this.singletonObjects.put(beanName, singletonObject)),	
		//singletonObjects中就不会有serviceA。
		Object singletonObject = this.singletonObjects.get(beanName);
		//判断会通过 第二个判断条件是在getSingleton方法的beforeSingletonCreation(beanName)设置的 最开始实例化A时会调用
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			synchronized (this.singletonObjects) {
				//这里也获取不到,因为在最开始servcieA实例化时的addSingletonFactory方法会执行
				//this.earlySingletonObjects.remove(beanName);
				singletonObject = this.earlySingletonObjects.get(beanName);
				//判断会进入
				if (singletonObject == null && allowEarlyReference) {
					//这里能获取到,因为在servcieA实例化时的addSingletonFactory方法会执行
					//this.singletonFactories.put(beanName, singletonFactory);
					ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
					if (singletonFactory != null) {
						//执行这个方法会执行那个lambda表达式 () -> getEarlyBeanReference(beanName, mbd, bean)
						//获取到serviceA
						singletonObject = singletonFactory.getObject();
						this.earlySingletonObjects.put(beanName, singletonObject);
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		//获取到返回 此时serviceB就可以得到serviceA了,然后继续创建serviceB
		return singletonObject;
	}

总结
实例化过程:
1.finishBeanFactoryInitialization(beanFactory);
2.beanFactory.preInstantiateSingletons();
3.getBean(beanName);
4.doGetBean
在这里插入图片描述

这里实例化serviceA时找不到,但是在实例化注入的serviceB时又需要实例化serviceB注入的serviceA,这时就可以获取到
serviceA了。

5.
在这里插入图片描述
这里的getSingleton方法在执行时执行到**singletonObject = singletonFactory.getObject()**时就会执行lambda表达式的createBean方法。
依赖关系的处理是在createBean方法的doCreateBean方法的populateBean方法
6.doCreateBean
在这里插入图片描述
关键方法

addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

这个方法在实例化serviceA,populateBean之前,把serviceA及关联的ObjectFactory(lambda表达式)加到了singletonFactories,这样serviceB实例化时需要实例化servcieA,在doGetBean一开始的getSingleton方法会获取到serviceA,这样就能保证serviceB能创建完成,然后再创建serviceA。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/615316.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

什么是Linux shell—一个简单的案例

一句话概括&#xff1a;简单来说脚本就是将需要执行的命令保存到文本中&#xff0c;按照顺序执行&#xff08;由上往下执行&#xff09;&#xff0c;shell脚本:shell脚本就是一些命令的集合。 一、创建第一个Shell脚本&#xff1a;输出helloworld 1&#xff0e;脚本格式 脚本…

(转载)基于遗传模拟退火的聚类算法(matlab实现)

1 理论基础 1.1 模糊聚类分析 模糊聚类是目前知识发现以及模式识别等诸多领域中的重要研究分支之一。随着研究范围的拓展&#xff0c;不管是科学研究还是实际应用&#xff0c;都对聚类的结果从多方面提出了更高的要求。模糊C-均值聚类(FCM)是目前比较流行的一种聚类方法。该…

【不单调的代码】还在嫌弃Ubuntu终端?快来试试做些Ubuntu终端的花式玩法。

&#x1f38a;专栏【​​​​​​​不单调的代码】 &#x1f354;喜欢的诗句&#xff1a;更喜岷山千里雪 三军过后尽开颜。 &#x1f386;音乐分享【Love Story】 &#x1f970;大一同学小吉&#xff0c;欢迎并且感谢大家指出我的问题&#x1f970; 注意&#xff1a; 本文是在…

【Protobuf速成指南】Protobuf快速上手

文章目录 1.0版本一、编写.proto文件1.文件规范&#xff1a;2.注释方式&#xff1a;3.指定proto3语法&#xff1a;4.package申明符5.定义message6.编写消息字段①类型对照表②唯一编号 二、编译.proto文件1. 编译指令2.源码分析 三、序列化和反序列化的使用四、小结 1.0版本 本…

TCP连接管理与UDP协议

一、TCP的连接管理 1.TCP包头 2.连接的建立——“三次握手” TCP 建立连接的过程叫做握手。 采用三报文握手&#xff1a;在客户和服务器之间交换三个 TCP 报文段&#xff0c;以防止已失效的连接请求报文段突然又传送到了&#xff0c;因而产生 TCP 连接建立错误。 3.连接的释放…

【智能座舱】— 看上海车展,高端品牌变局,时代变天早开始,40项智能化创新技术解密~

大家好,欢迎阅读本期文章,我们将带您解读一份极具实用价值的汽车研究报告。本期将聚焦于2023年上海车展,解密未来座舱发展技术脉络 在本期文章中,我们将深度探讨这些前沿技术的应用,为您呈现未来汽车的全景图。我们相信,这将有助于您更加准确地选择适合自己的新能源汽车…

Kafka测试实战:从基础入门到高阶技巧(建议收藏)

Kafka是一种高吞吐量的分布式发布-订阅消息系统&#xff0c;它可以处理所有活动流数据。在进行Kafka的测试时&#xff0c;我们需要验证生产者能否成功发送消息&#xff0c;消费者能否成功消费消息。在本文中&#xff0c;我们将使用Python来进行Kafka的测试&#xff0c;并提供从…

pytorch实战 -- 数据加载和处理

Pytorch提供了许多工具来简化和希望数据加载&#xff0c;使代码更具可读性。这里将专门讲述transforms数据预处理方法&#xff0c;即数据增强。 数据增强又称为数据增广、数据扩增&#xff0c;它是对训练集进行变换&#xff0c;使训练集更丰富&#xff0c;从而让模型更具泛化能…

POWERBUILDER中高级学习提纲

Chengg0769 2012年 版权来自于&#xff1a; www.mis2erp.com http://blog.csdn.net/chengg0769 http://www.haojiaocheng.cc 转载请保留以上信息 这个提纲的来由&#xff1a; 当时&#xff0c;有个朋友说因伤疗养&#xff0c;想从过去做维护变为做开发&#xff0c;想学习…

从裸机启动开始运行一个C++程序(四)

先序文章请看 从裸机启动开始运行一个C程序&#xff08;三&#xff09; 从裸机启动开始运行一个C程序&#xff08;二&#xff09; 从裸机启动开始运行一个C程序&#xff08;一&#xff09; 跳转 前面我们介绍过&#xff0c;8086CPU总是在执行CS:IP所对应的内存位置的指令&…

签章那些事 -- 让你全面了解签章的流程

前言 随着通信、互联网技术的发展&#xff0c;人们接触到的信息纷繁复杂&#xff0c;信息的真真假假让人难以辨认。在严肃性场合&#xff0c;比如电子合同、电子证照等&#xff0c;必须有一种手段确保信息的完整性和真实性&#xff0c;这时签章就派上了用场。 签章的技术原理并…

高频面试八股文用法篇(四) 乐观锁和悲观锁的例子

目录 什么是乐观锁和悲观锁 乐观锁的实现方式主要有两种&#xff1a;CAS机制和版本号机制 1&#xff09;CAS&#xff08;Compare And Swap&#xff09; (2)版本号 乐观锁适用场景 乐观锁和悲观锁优缺点 功能限制 竞争激烈程度 什么是乐观锁和悲观锁 乐观锁&#xff1…

录音软件哪个好用?录音软件免费下载安装

案例&#xff1a;有没有好用的录音软件推荐&#xff1f; 【我想录制电脑上的音乐和音频会议&#xff0c;也想用电脑录制自己的歌声&#xff0c;有没有好用的电脑录音软件推荐&#xff1f;】 在日常生活和工作中&#xff0c;我们经常会遇到需要录音的场景&#xff0c;比如会议…

几行代码,轻松教你用Java 将 Word 文档转换为 HTML

Aspose.Words 是一种高级Word文档处理API&#xff0c;用于执行各种文档管理和操作任务。API支持生成&#xff0c;修改&#xff0c;转换&#xff0c;呈现和打印文档&#xff0c;而无需在跨平台应用程序中直接使用Microsoft Word。此外&#xff0c; Aspose API支持流行文件格式处…

基于windows环境利用VS下通过Linux环境下服务器进行UDP通信交流

目录 前言 Linux udpServer.cc udpServer.hpp makefile windows 细节1 -- 头文件引入 细节2 -- 固定写法 细节3 -- 结束后清理 细节4 -- socket返回值接受 细节5 -- 套接字创建(一样的写法) 细节6 -- 填写sockaddr_in结构体 细节7 -- 接发收数据 细节8 -- 报错信…

自学黑客(网络安全),一般人我还是劝你算了

一、自学网络安全学习的误区和陷阱 1.不要试图先成为一名程序员【以编程为基础的学习】再开始学习 我在之前的回答中&#xff0c;我都一再强调不要以编程为基础再开始学习网络安全&#xff0c;一般来说&#xff0c;学习编程不但学习周期长&#xff0c;而且实际向安全过渡后可…

操作系统复习2.2.4-作业/进程调度算法

算法 FCFS先来先服务、SJF短作业优先、HRRN高响应比优先、时间片轮转、优先级调度、多级反馈队列调度 FCFS先来先服务 公平&#xff0c;按照到达先后顺序进行服务 用于作业时&#xff0c;考虑哪个作业先到达后备队列 用于进程时&#xff0c;考虑哪个进程先到达就绪队列 非抢…

引领科技潮流:国产化操作系统和CPU的市场竞争力

随着国家科技实力的不断提升&#xff0c;国产化已经成为了中国科技发展的一大趋势。在多个领域&#xff0c;中国企业正在加快国产替代进程&#xff0c;取得了一定的成果。 2、国产化现状 2.1、操作系统 中国国产操作系统包括以下几种&#xff1a; 麒麟操作系统(Kylin OS):由…

2023 某行业-CTF

文章目录 miscmisc1misc2misc3misc4 WebWeb1Web2Web3Web5 misc misc1 %26%2365%3B%26%2376%3B%26%2390%3B%26%23107%3B%26%23121%3B%26%2389%3B%26%2377%3B%26%2366%3B%26%2390%3B%26%2351%3B%26%2355%3B%26%23120%3B%26%23102%3B%26%23119%3B%26%2369%3B%26%2371%3B%26%2310…