IoC源码分析——singleton bean创建与循环依赖

文章目录

    • 概要
    • 主流程
    • bean的创建
    • 循环依赖

概要

容器初始化时,会创建单例bean,本文主要关注单例bean是如何创建的,并说明源码中是如何解决循环依赖的
代码入口

@Test
	public void testIoC() {
		// ApplicationContext是容器的高级接口,BeanFactory(顶级容器/根容器,规范了/定义了容器的基础行为)
		// Spring应用上下文,官方称之为 IoC容器(错误的认识:容器就是map而已;准确来说,map是ioc容器的一个成员,
		// 叫做单例池, singletonObjects,容器是一组组件和过程的集合,包括BeanFactory、单例池、BeanPostProcessor等以及之间的协作流程)

		/**
		 * Ioc容器创建管理Bean对象的,Spring Bean是有生命周期的
		 * 构造器执行、初始化方法执行、Bean后置处理器的before/after方法、:AbstractApplicationContext#refresh#finishBeanFactoryInitialization
		 * Bean工厂后置处理器初始化、方法执行:AbstractApplicationContext#refresh#invokeBeanFactoryPostProcessors
		 * Bean后置处理器初始化:AbstractApplicationContext#refresh#registerBeanPostProcessors
		 */

		// ClassPathXmlApplicationContext-> AbstractXmlApplicationContext
		// -> AbstractRefreshableConfigApplicationContext ->AbstractRefreshableApplicationContext
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
		NormalBean normalBean = applicationContext.getBean(NormalBean.class);
		System.out.println(normalBean);
	}

以下是spring源码启动的主流程入口

@Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
			/**
			 * 刷新前的预处理
			 * 表示在真正做refresh操作之前需要准备做的事情
			 * 	设置spring容器的启动时间
			 * 	开启活跃状态,撤销关闭状态
			 * 	验证环境信息里一些必须存在的属性等
			 */
			prepareRefresh();
			/**
			 * 获取BeanFactory;默认实现是DefaultListableBeanFactory
			 * 加载BeanDefition 并注册到 BeanDefitionRegistry
			 */
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
			/**
			 * 第三步: BeanFactory的预准备⼯作(BeanFactory进⾏⼀些设置,⽐如context的类加
			 * 载器等)
			 */
			prepareBeanFactory(beanFactory);

			try {
				// 第四步: BeanFactory准备⼯作完成后进⾏的后置处理⼯作
				postProcessBeanFactory(beanFactory);

				StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
				// Invoke factory processors registered as beans in the context.
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation.
				// 第六步:注册BeanPostProcessor(Bean的后置处理器),在创建bean的前后等执⾏
				registerBeanPostProcessors(beanFactory);
				beanPostProcess.end();

				// Initialize message source for this context.
				// 第七步:初始化MessageSource组件(做国际化功能;消息绑定,消息解析);
				initMessageSource();

				// Initialize event multicaster for this context.
				// 第⼋步:初始化事件派发器
				initApplicationEventMulticaster();

				// Initialize other special beans in specific context subclasses.
				// 第九步:⼦类重写这个⽅法,在容器刷新的时候可以⾃定义逻辑
				onRefresh();

				// Check for listener beans and register them.
				// 第⼗步:注册应⽤的监听器。就是注册实现了ApplicationListener接⼝的监听器bean
				registerListeners();

				// Instantiate all remaining (non-lazy-init) singletons.
				/*
				  第⼗⼀步:
				  初始化所有剩下的⾮懒加载的单例bean
				  初始化创建⾮懒加载⽅式的单例Bean实例(未设置属性)
				  填充属性
				  初始化⽅法调⽤(⽐如调⽤afterPropertiesSet⽅法、 init-method⽅法)
				  调⽤BeanPostProcessor(后置处理器)对实例bean进⾏后置处
				*/
				finishBeanFactoryInitialization(beanFactory);

				// Last step: publish corresponding event.
				/*
					第⼗⼆步:
					完成context的刷新。主要是调⽤LifecycleProcessor的onRefresh()⽅法,并且发布事
					件 (ContextRefreshedEvent)
				*/
				finishRefresh();
			} catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}

				// Destroy already created singletons to avoid dangling resources.
				destroyBeans();

				// Reset 'active' flag.
				cancelRefresh(ex);

				// Propagate exception to caller.
				throw ex;
			} finally {
				// Reset common introspection caches in Spring's core, since we
				// might not ever need metadata for singleton beans anymore...
				resetCommonCaches();
				contextRefresh.end();
			}
		}
	}

主流程

关于bean的三级缓存,DefaultSingletonBeanRegistry中的变量,如下

/**
* 一级缓存:单例(对象)池,这里面的对象都是确保初始化完成,可以被正常使用的
* 它可能来自3级,或者2级
*/
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/**
* 三级缓存:单例工厂池,这里面不是bean本身,是它的一个工厂,未来调getObject来获取真正的bean
* 一旦获取,就从这里删掉,进入2级(发生闭环的话)或1级(没有闭环)
*/
private final Map<String, ObjectFactory<?>> singletonFactories = newHashMap<>(16);
/**
* 二级缓存:早期(对象)单例池,这里面都是半成品,只是有人用它提前从3级get出来,把引用
暴露出去
* 它里面的属性可能是null,所以叫早期对象,early!半成品
* 未来在getBean付完属性后,会调addSingleton清掉2级,正式进入1级
*/
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

关于单例bean的生命周期,其主要包括以下几个方面
在这里插入图片描述
各个部分的细节如下:
在这里插入图片描述

bean的创建

代码层面,主要关注以下几个方法
在这里插入图片描述
入口:org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons

public void preInstantiateSingletons() throws BeansException {
	if (logger.isTraceEnabled()) {
		logger.trace("Pre-instantiating singletons in " + this);
	}

	// Iterate over a copy to allow for init methods which in turn register new bean definitions.
	// While this may not be part of the regular factory bootstrap, it does otherwise work fine.
	List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

	// Trigger initialization of all non-lazy singleton beans...
	for (String beanName : beanNames) {
		RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
		// 是否抽象 是否单例 是否延迟创建
		if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
			if (isFactoryBean(beanName)) {
				Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
				if (bean instanceof FactoryBean) {
					FactoryBean<?> factory = (FactoryBean<?>) bean;
					boolean isEagerInit;
					if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
						isEagerInit = AccessController.doPrivileged(
								(PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit,
								getAccessControlContext());
					} else {
						isEagerInit = (factory instanceof SmartFactoryBean &&
								((SmartFactoryBean<?>) factory).isEagerInit());
					}
					if (isEagerInit) {
						getBean(beanName);
					}
				}
			} else {
				// 不是工厂bean,普通bean的创建
				getBean(beanName);
			}
		}
	}
	....
}

在这里插入图片描述

@Nullable
	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		// 判断一级缓存中是否存在
		Object singletonObject = this.singletonObjects.get(beanName);
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			synchronized (this.singletonObjects) {
			// 二级缓存中是否存在
				singletonObject = this.earlySingletonObjects.get(beanName);
				if (singletonObject == null && allowEarlyReference) {
				// 	再从三级缓存中获取
					ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
					if (singletonFactory != null) {
						singletonObject = singletonFactory.getObject();
						this.earlySingletonObjects.put(beanName, singletonObject);
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		return singletonObject;
	}

注意这里有个细节,先判断二级中缓存是否存在,不存在再从三级缓存中获取。这里很重要,也是为什么要有二级缓存存在的原因

在这里插入图片描述
触发函数式接口
在这里插入图片描述
下面看:AbstractAutowireCapableBeanFactory#createBean(String,RootBeanDefinition,Object[])

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @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);
		}
		Object bean = instanceWrapper.getWrappedInstance();
		Class<?> beanType = instanceWrapper.getWrappedClass();
		if (beanType != NullBean.class) {
			mbd.resolvedTargetType = beanType;
		}
		synchronized (mbd.postProcessingLock) {
			...
		}

		// 是否单例 是否允许循环引用 是否 正在创建->提前暴露
		boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
				isSingletonCurrentlyInCreation(beanName));
		if (earlySingletonExposure) {
			...
			// 放入到三级缓存中
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}
		// Initialize the bean instance.
		Object exposedObject = bean;
		try {
			// bean属性填充
			populateBean(beanName, mbd, instanceWrapper);
			// 调用初始化方法,应用BeanPostProcessor方法
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		} catch (Throwable ex) {
			..
		}
		...

		return exposedObject;
	}

到这里,初始化bean的主流程就跑完了,在没有循环依赖的情况下,bean的正常创建就是经历了以上主流程代码。下面仔细分析下org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#getEarlyBeanReference

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

bean在用构造函数创建后,此时还没有设置属性值,会提前把一个函数式接口放入到三级缓存中,上面的getEarlyBeanReference方法,就是在addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));放入到三级缓存中。进入到里面可以看到跟创建代理对象有关。所以此处将接口放入到三级缓存主要有以下两个作用:

  1. 提前暴露正在创建的对象,如果有其他对象引用了该对象,那么就可以直接从三级缓存中获取
  2. 如果该对象需要被增强,要被创建代理对象,那么该函数式接口也能保证,其他对象依赖的是增强后的对象

循环依赖

下面从源码分析,spring是如何解决循环依赖的。
循环依赖涉及到三级缓存的的变化,下面围绕三级缓存的变化梳理主要流程

有配置文件如下

<!--循环依赖BeanA依赖BeanB -->
<bean id="userServiceImplA" class="com.spring.test.impl.UserServiceImplA">
<property name="userServiceImplB" ref="userServiceImplB"/>
</bean>
<!--循环依赖BeanB依赖BeanA -->
<bean id="userServiceImplB" class="com.spring.test.impl.UserServiceImplB">
<property name="userServiceImplA" ref="userServiceImplA"/>
</bean>

上面看出对象A引用了对象B,同时对象B也引用了对象A,对象A、B之间存在循环依赖

在这里插入图片描述
对象B在设置属性值A时,会调getBean方法,从容器中获取对象,从而会重新走一遍上面的主流程,入口如下
org.springframework.beans.factory.support.BeanDefinitionValueResolver#resolveReference
在这里插入图片描述
在走一遍主流程时,在org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean) 方法中,会发现三级缓存中已经存在对象A了,就直接从三级缓存中获取
在这里插入图片描述
这里要注意的时,三级中A的会被移除,从而转移到二级缓存中,这里为什么要从三级缓存中移除呢?假如有个对象C也引用了A,并且对象A是需要创建动态代理的,如果不从3级缓存中进行移除,那么A对象就会被重复执行到三级缓存中的函数式接口,这样就是产生多个A的代理对象。从三级缓存中移除,并转移到二级缓存中,这样,当C也需要A时,直接从二级缓存中进行获取就可以了,就不会造成创建多个A对象的代理对象。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

中霖教育怎么样?税务专业可以考哪些证书?

在税务专业领域&#xff0c;专业技能的认证对职业发展至关重要。以下为税务专业相关可以考的证书&#xff1a; 1. 注册税务师资格证书&#xff1a;该证书是税务专业人士的关键资质&#xff0c;使持证者可以从事税务相关工作。 2. 会计职称证书&#xff1a;会计系列证书分为初…

即时通讯平台项目测试(主页面)

http://8.130.98.211:8080/login.html项目访问地址&#xff1a;即时通讯平台http://8.130.98.211:8080/login.html 本篇文章进行项目主页面的测试。 在测试前需要先对待测内容进行分类&#xff0c;按照功能进行分类可以分为&#xff1a;个人信息设置、发送/接收消息、添加好友…

python-25-零基础自学python-处理异常三兄弟try-except-else

学习内容&#xff1a;《python编程&#xff1a;从入门到实践》第二版第十章 知识点&#xff1a; 程序异常如何处理&#xff1f;try-except-else try-尝试可能引起错误的步骤 except-错误步骤发生&#xff0c;打印一些需要用户知道的信息&#xff0c;没有就pass else-错误不…

【密码学】公钥密码的基本概念

在先前我写的密码学体制文章中谈到&#xff0c;现代密码学分为两大体制&#xff0c;介绍了一些有关对称密码体制诸如流密码和分组密码的内容。本文的主要内容则切换到公钥密码体制&#xff08;又称非对称密码体制&#xff09;&#xff0c;简述了公钥密码体制的基本思想和应用方…

MFC常见问题解决

文章目录 1. 单文档程序初始化显示设置问题解决方案 2. MFC中控件响应出错 1. 单文档程序初始化显示设置 问题 在Microsoft Foundation Classes (MFC) 中&#xff0c;单文档应用程序&#xff08;SDI&#xff09;的初始化时默认并不设置为最大显示。但你可以通过编程方式在程序…

传神论文中心|第16期人工智能领域论文推荐

在人工智能领域的快速发展中&#xff0c;我们不断看到令人振奋的技术进步和创新。近期&#xff0c;开放传神&#xff08;OpenCSG&#xff09;社区发现了一些值得关注的成就。传神社区本周也为对AI和大模型感兴趣的读者们提供了一些值得一读的研究工作的简要概述以及它们各自的论…

MiniGPT-Med 通用医学视觉大模型:生成医学报告 + 视觉问答 + 医学疾病识别

MiniGPT-Med 通用医学视觉大模型&#xff1a;生成医学报告 视觉问答 医学疾病识别 提出背景解法拆解 论文&#xff1a;https://arxiv.org/pdf/2407.04106 代码&#xff1a;https://github.com/Vision-CAIR/MiniGPT-Med 提出背景 近年来&#xff0c;人工智能&#xff08;AI…

Mybatis的学习

什么是MyBatis? MyBatis 是一款优秀的持久层框架&#xff0c;用于简化JDBC 开发 MyBatis 本是 Apache 的一个开源项目iBatis,2010年这个项目由apache software foundation 迁移到了google code&#xff0c;并且改名为MyBatis。2013年11月迁移到Github 官网:https://mybatis…

Python的语言特性

1&#xff0c;python是动态语言 在编译期间就确定变量类型的语言是静态语言 在运行期间才知道变量类型的是动态语言 2&#xff0c;python是强类型语言 不同类型的变量是否允许隐式转换

简过网:哪些证书适合上班族考?这些证书挂着就能挣钱!

考证不一定暴富&#xff0c;但找工作时一定吃香 今天给大家整理几个比较吃香的证书&#xff01; 不仅含金量高还可以领补贴 建议大家有时间都考一考 普通人就冲一冲&#xff1a;教资、二建、一建、一造、监理&#xff01;&#xff01; ​ ★ 教师资格证书 报名时间&#x…

STM32G4 DMA的使用(寄存器开发)

下面以STM32G474为例&#xff0c;使用DMA来存储USART1的接收数据。 1. 查看硬件支持 首先查看要使用的DMA支持的通道数&#xff0c;在手册中有如下说明。 根据上图可以看到&#xff0c;对于不同的设备类型有不同的DMA通道数量。设备类型分类如下图所示。 我使用的是STM32G474…

获取天气数据

获取天气数据其实是一个简单的HTTP接口&#xff0c;根据用户输入的adcode&#xff0c;查询目标区域当前/未来的天气数据&#xff0c;数据来源是中国气象局。 第一步&#xff0c;申请”web服务 API”密钥&#xff08;Key&#xff09;&#xff1b; 链接: 首页 | 高德控制台 (am…

Redis基础教程(二十):Java使用Redis

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; &#x1f49d;&#x1f49…

架构师机器学习操作 (MLOps) 指南

MLOps 是机器学习操作的缩写&#xff0c;是一组实践和工具&#xff0c;旨在满足工程师构建模型并将其投入生产的特定需求。一些组织从一些自主开发的工具开始&#xff0c;这些工具在每次实验后对数据集进行版本控制&#xff0c;并在每个训练周期后对检查点模型进行版本控制。另…

Mybatis的优缺点及适用场景?

目录 一、什么是Mybatis&#xff1f; 二、Mybatis框架的特点 三、Mybatis框架的优点&#xff1f; 四、MyBatis 框架的缺点&#xff1f; 五、MyBatis 框架适用场合&#xff1f; 六、代码示例 1. 配置文件 mybatis-config.xml 2. 映射文件 UserMapper.xml 3. Java 代码…

了解劳动准备差距:人力资源专业人员的战略

劳动准备差距是一个紧迫的问题&#xff0c;在全球人事部门回应&#xff0c;谈论未开发的潜力和错过的机会。想象一下&#xff0c;人才和需求之间的悬崖之间有一座桥&#xff0c;这促使雇主思考&#xff1a;我们是否为员工提供了足够的设备来应对未来的考验&#xff1f; 这种不…

kali安装vulhub遇到的问题及解决方法(docker及docker镜像源更换)

kali安装vulhub&#xff1a; 提示&#xff1a;项目地址 https://github.com/vulhub/vulhub 项目安装&#xff1a; git clone https://github.com/vulhub/vulhub.git 安装docker 提示&#xff1a;普通用户请使用sudo&#xff1a; 首先安装 https 协议、CA 证书 apt-get in…

【昆工主办|7月昆明】第三届绿色建筑、土木工程与智慧城市国际会议(GBCESC 2024)

随着全球城市化进程的加速&#xff0c;绿色建筑、土木工程与智慧城市等议题逐渐成为了行业内外关注的焦点。在这一背景下&#xff0c;第三届绿色建筑、土木工程与智慧城市国际会议&#xff08;GBCESC 2024&#xff09;的召开&#xff0c;无疑将为相关领域的研究者、学者及从业者…

如何保障生物制药中试验网和办公网之间的跨网安全文件交换数据?

在针对数据化大环境下&#xff0c;生物制药企业的数据安全尤为关键&#xff0c;尤其是试验网与办公网之间的数据交换。这些数据不仅包含新药品研发成果、临床试验数据&#xff0c;还有健康医疗数据等&#xff0c;都是企业的核心竞争力和商业秘密 。因此&#xff0c;安全地进行跨…

flask模块化、封装使用缓存cache(flask_caching)

1.安装flask_caching库 pip install flask_caching 2.创建utils Python 软件包以及cache_helper.py 2.1cache_helper.py代码 from flask_caching import Cachecache Cache()class CacheHelper:def __init__(self, app, config):cache.init_app(app, config)staticmethoddef…