Spring核心源码-如何解决循环依赖

news2024/9/23 13:39:49

假设有两个类A和B
B是A的成员变量,A也是B的成员变量。
假设类A的bean为a,类B的bean为b。且IOC容器先处理A。

熟悉Spring容器初始化的同学,应该都知道,容器初始化的过程中,bean的创建是如下触发的:
在这里插入图片描述
getBean 的时候发现不存在,就去 createBean
bean的创建是在 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean 完成的。

doCreateBean 大致可以分为三步:

1、实例化bean:createBeanInstance
2、填充属性:populateBean
3、初始化bean:initializeBean

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {
		// Instantiate the bean.
		BeanWrapper instanceWrapper = null;
		// 实例化bean
		if (instanceWrapper == null) {
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
		......
		boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
				isSingletonCurrentlyInCreation(beanName));
		if (earlySingletonExposure) {
			// 放入3级缓存
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}
		......
		try {
			// 填充属性
			populateBean(beanName, mbd, instanceWrapper);
			// 初始化bean
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}
		...... 
		return exposedObject;
	}

其中第三步,只是Spring框架提供的 InitializingBean 接口的扩展,用于设置完properties之后做一些动作,对循环依赖没有影响。
循环依赖的处理只发生在第一步和第二步。

从以上代码可以看到,当A类的一个单例对象a被实例化之后,被立即放在了3级缓存内,具体的代码如下:

	/**
	 * Add the given singleton factory for building the specified singleton
	 * if necessary.
	 * <p>To be called for eager registration of singletons, e.g. to be able to
	 * resolve circular references.
	 * @param beanName the name of the bean
	 * @param singletonFactory the factory for the singleton object
	 */
	protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
		Assert.notNull(singletonFactory, "Singleton factory must not be null");
		synchronized (this.singletonObjects) {
			if (!this.singletonObjects.containsKey(beanName)) {
				// 放入3级缓存
				this.singletonFactories.put(beanName, singletonFactory);
				// 从2级缓存中移除(确保2级缓存没有)
				this.earlySingletonObjects.remove(beanName);
				// 标记这个bean已经创建过
				this.registeredSingletons.add(beanName);
			}
		}
	}

在给a设置属性B的时候,去对B进行 getBean ,发现不存在,也会对B进行 createBean
类B的对象b,在实例化之后,也会进行属性的设置,会对类A进行 getBean ,这部分就有了差异。

@SuppressWarnings("unchecked")
	protected <T> T doGetBean(
			String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
			throws BeansException {

		String beanName = transformedBeanName(name);
		Object beanInstance;
		
		// 这里去获取A的单例bean
		// Eagerly check singleton cache for manually registered singletons.
		Object sharedInstance = getSingleton(beanName);
		if (sharedInstance != null && args == null) {
			if (logger.isTraceEnabled()) {
				if (isSingletonCurrentlyInCreation(beanName)) {
					logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
							"' that is not fully initialized yet - a consequence of a circular reference");
				}
				else {
					logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
				}
			}
			beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
		}

getSingleton 的代码如下:

	/**
	 * Return the (raw) singleton object registered under the given name.
	 * <p>Checks already instantiated singletons and also allows for an early
	 * reference to a currently created singleton (resolving a circular reference).
	 * @param beanName the name of the bean to look for
	 * @param allowEarlyReference whether early references should be created or not
	 * @return the registered singleton object, or {@code null} if none found
	 */
	@Nullable
	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		// Quick check for existing instance without full singleton lock
		// 从1级缓存拿
		Object singletonObject = this.singletonObjects.get(beanName);
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			// 从2级缓存拿
			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) {
							// 从3级缓存拿
							ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
							if (singletonFactory != null) {
								singletonObject = singletonFactory.getObject();
								// 放入2级缓存
								this.earlySingletonObjects.put(beanName, singletonObject);
								// 从3级缓存中移除
								this.singletonFactories.remove(beanName);
							}
						}
					}
				}
			}
		}
		return singletonObject;
	}

因为前面类A执行 doCreateBean 的时候,已经放进了3级缓存,所以b在设置属性的时候,是能拿得到a的。
这里的拿到的a还仅仅是执行了实例化的,并没有设置完属性。

b在执行完第二步设置属性,第三步初始化之后,又返回到a的第二步设置属性,第三步初始化。
至此,类A的对象a和类B的对象b,都已经创建成功。

最终总结流程图如下:
在这里插入图片描述

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

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

相关文章

Unity可视化Shader工具ASE介绍——4、ASE的自定义模板使用

大家好&#xff0c;我是阿赵。   继续介绍Unity可视化Shader编辑工具ASE。之前的文章介绍了在ASE里面可以选择不同的Shader类型。这一篇来继续探讨一下&#xff0c;这些Shader类型究竟是什么。 一、所谓的Shader类型是什么 选择不同的Shader类型&#xff0c;会出现不同的选项…

Unity 之 EditorGUILayout.BeginHorizontal/EndHorizontal异常报错问题

报错内容&#xff1a; 缘由&#xff1a;由于在EditorGUILayout.EndHorizontal()之前执行了类似打开窗口的逻辑 解决办法&#xff1a; 在EditorGUILayout.EndHorizontal()之前执行GUIUtility.ExitGUI();

vulnhub_driftingblues7靶机渗透测试

Driftingblues7靶机 文章目录 Driftingblues7靶机信息收集web渗透获取权限另外思路靶机总结 信息收集 使用nmap扫描得到靶机ip为192.168.78.174&#xff0c;开放发端口有很多&#xff0c;而且开放了443端口&#xff0c;所以访问网站是需要https协议的 再对该网站进行目录扫描&…

10-09 周一 图解机器学习之深度学习感知机学习

10-09 周一 图解机器学习之深度学习感知机学习 时间版本修改人描述2023年10月9日14:13:20V0.1宋全恒新建文档 简介 感知机是神经网络中的概念&#xff0c;1958年被Frank Rosenblatt第一次引入。感知机作为一种基本的神经网络模型&#xff0c;它模拟了人脑神经元的工作原理。感…

解决Feign的自定义解码器在接口返回值为void时不执行的问题

项目的接口有一个全局的响应包装器&#xff0c;将接口的所有返回&#xff0c;包括各种类型如List、Entity&#xff0c;或者void&#xff0c;以及抛出的异常&#xff0c;封装成统一的结构给到前端&#xff0c;所以在使用Feign发起远程调用的时候&#xff0c;需要一个自定义的解码…

详解 ElasticSearch 基础教程

&#x1f339; 分享 ElasticSearch 基础&#xff0c;请指教。&#x1f339;&#x1f339; 如你对技术也感兴趣&#xff0c;欢迎交流。&#x1f339;&#x1f339;&#x1f339; 如有对阁下帮助&#xff0c;请&#x1f44d;点赞&#x1f496;收藏&#x1f431;‍&#x1f3cd;分…

Python 自动化Web测试

限于作者水平有限&#xff0c;以下内容可能是管窥之见&#xff0c;希望大家高抬贵手&#xff0c;且让我斗胆抛砖引玉。 公司产品迪备主要是通过网页操作来进行数据库的备份与恢复&#xff0c;监控与管理&#xff0c;因此在测试的过程中&#xff0c;可以用python测试脚本来模拟…

运维知识点汇总

一.公共基础 linux常用目录 链接一 链接二 linux系统启动 链接一 链接二 LVM 链接一 磁盘挂载 链接一 文件权限 链接一 二.VLAN详解 链接 三.中间件 单体部署&#xff1a; 优点&#xff1a; &#xff08;1&#xff09;小团队成型即可完成开发-测试-上线&am…

3D机器视觉:解锁未来的立体视野

原创 | 文 BFT机器人 机器视觉领域一直在不断演进&#xff0c;从最初的二维图像处理&#xff0c;逐渐扩展到了更复杂的三维领域&#xff0c;形成了3D机器视觉。3D机器视觉技术的涌现为计算机系统带来了全新的感知和理解能力&#xff0c;这一领域的发展正日益受到广泛关注。本文…

Android系统为什么采用Binder作为IPC机制

Android系统提供了多种进程间通信&#xff08;IPC&#xff09;的机制&#xff0c;用于不同进程之间的数据交换和通信。以下是Android系统中常用的几种IPC机制&#xff1a; Intent&#xff1a;Intent是Android系统中常用的一种进程间通信方式。通过发送Intent&#xff0c;可以在…

链表去重Java

去除掉链表中重复的元素,两种方法: static class ListNode{private int val;private ListNode next;public ListNode(int val, ListNode next) {this.val val;this.next next;}Overridepublic String toString() {return "ListNode{" "val" val ",…

【jvm】程序计数器

目录 一、介绍二、作用三、示例3.1 代码3.2 javap -v Test1.class3.2.1 操作指令及地址 一、介绍 1.jvm中的程序计数寄存器中&#xff08;program counter register&#xff09;&#xff0c;register的命名源于cpu的寄存器&#xff0c;寄存器存储指令相关的现场信息 2.cpu只有把…

C++学习之指针和数组

指针和一维数组 一个数组包含若干元素&#xff0c;每个数组元素都在内存中占用存储单元&#xff0c;它们都有相应的地址。指针变量既可以指向变量&#xff0c;当然也可以指向数组元素。所谓数组元素的指针就是数组元素的地址。 eg&#xff1a; int a[6]; //定义一个整数数组a…

web:[HCTF 2018]admin

题目 点击页面显示如下 点击hctf显示 没有账号&#xff0c;先注册一个 随便注册一个试试看 输入账号密码显示如下 页面没有其他的提示&#xff0c;查看源代码 这里提示不是admin 先注册一个admin账号试一下 显示admin已经被注册了&#xff0c;所以知道用户名为admin&#xff0…

【图像分割】SAM、FastSAM与MobileSAM原理

文章目录 前言&#xff1a;Segment Anything一、FastSAM二、MobileSAM框架实验 总结 前言&#xff1a;Segment Anything Meta 今年发布了图像分割模型 Segment Anything Model (SAM) 。SAM 已经学会了关于物体的一般概念&#xff0c;可以为任何图像或视频中的任何物体生成 mas…

基于共生生物优化的BP神经网络(分类应用) - 附代码

基于共生生物优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码 文章目录 基于共生生物优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码1.鸢尾花iris数据介绍2.数据集整理3.共生生物优化BP神经网络3.1 BP神经网络参数设置3.2 共生生物算法应用 4.测试结果…

JDBC-day03(BLOB类型字段,批量插入)

四&#xff1a;操作BLOB类型字段 1.MySQL BLOB类型 在MySQL中&#xff0c;BLOB是一个二进制大型对象&#xff0c;是一个可以存储大量数据的容器&#xff0c;它能容纳不同大小的数据。可以用来存储图片&#xff0c;视频等 插入BLOB类型的数据必须使用PreparedStatement&#x…

redis,mongoDB,mysql,Elasticsearch区别

Redis&#xff1a; Redis是一种高性能键值存储数据库&#xff0c;基于内存操作&#xff0c;支持数据持久化&#xff0c;支持数据类型丰富灵活&#xff0c;如字符串、哈希、列表、集合、有序集合等。Redis还提供了订阅/发布、事务、Lua脚本、主从同步等功能&#xff0c;适用于访…

学习记忆——数学篇——案例——代数——函数——一元二次函数

记忆宫殿法 一元二次函数&#xff1a; y a x 2 b x c yax^2bxc yax2bxc a &#xff1e; 0 a&#xff1e;0 a&#xff1e;0&#xff0c;开口向上&#xff1b; a &#xff1c; 0 a&#xff1c;0 a&#xff1c;0&#xff0c;开口向下&#xff1b; x − b 2 a x-\frac{b}{2a…

2023-10-09 python-使用psd_tools-读取psd信息及导出图层图片-记录

摘要: 2023-10-09 python-使用psd_tools-读取psd信息及导出图层图片-记录 相关文档: Usage — psd-tools 1.9.28 documentation 读取psd的信息: py代码: from psd_tools import PSDImagepsd PSDImage.open(example.psd) #psd PSDImage.open(one.psd)print(psd)for layer in …