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

news2025/4/20 14:13:09

文章目录

    • 概要
    • 主流程
    • 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对象的代理对象。

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

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

相关文章

qcom 平台efuse机器抓取dump log的方法

引言&#xff1a; qcom 平台机器&#xff0c;一旦efuse后机器将无法抓取dump log qcom 原文&#xff1a; efuse机器抓取dump log的方法如下&#xff1a; 一、修改配置文件&#xff1a; 把kamorta_debugpolicy.xml 在配置了debugpolicy&#xff08;加入串号和打开开关&#x…

前端进阶全栈计划:Java基础语法

前言 本教程旨在帮助初学者系统地掌握Java的基础知识。我们将从Java的基本语法开始&#xff0c;逐步深入到面向对象编程、异常处理、多线程编程等核心概念。无论你是编程新手&#xff0c;还是希望夯实基础的开发者&#xff0c;这份指南都将带你走进Java的世界&#xff0c;打下坚…

安全防御---防火墙实验1

安全防御—防火墙实验1 一、实验拓扑与要求 要求&#xff1a; 1、DMZ区内的服务器&#xff0c;办公区仅能在办公时间内&#xff08;9&#xff1a;00-18:00)可以访问&#xff0c;生产区的设备全天可以访问 2、生产区不允许访问互联网&#xff0c;办公区和游客区允许访问互联网 …

TQSDRPI开发板教程:编译openwifi工程

本例程基于SDRPI开发板&#xff0c;在Ubuntu中使用vivado编译openwifi工程&#xff0c;最终生成BOOT.BIN文件。需要拥有安装vivado2021.1版本的ubuntu系统或虚拟机。 首先需要下载openwifi的编译文件&#xff0c;可以在GitHub中搜索openwifi-hw&#xff0c;网址如下所示&#…

Python基础教学之一:入门篇——迈入编程世界的第一步

Python基础教学之一&#xff1a;入门篇——迈入编程世界的第一步 一、Python简介&#xff1a;历史与现状 Python&#xff0c;一种解释型、高级和通用的编程语言&#xff0c;由Guido van Rossum在1989年圣诞节期间创造&#xff0c;并于1991年首次发布。设计哲学强调代码的可读性…

Centos9安装部署及静态ip配置方案

一、获取centos9的iso镜像 去官网 点击download 官网 点击x86&#xff0c;下载centos9 二、创建虚拟机 1、点击新建虚拟机 2、选择自定义 下一步 3、点击下一步 4、选择稍后安装操作系统 点击下一步 5、选择linux 选择要安装的centos 版本 这里选择centos7 6、设置虚拟…

顺序结构 ( 五 ) —— 数据输入输出 【互三互三】

文章目录 &#x1f341;序 &#x1f341;一、字符输入函数getchar &#x1f341;二、字符输出函数putchar &#x1f341;三、通过cout流输出数据 &#x1f341;四、通过cin流读入数据 &#x1f341;五、格式化输入函数scanf &#x1f341;六、格式化输出函数printf &…

最简单的vue3组件之间传值

localStorage 是 HTML5 引入的一个 Web Storage API 的一部分&#xff0c;它允许网页在用户的浏览器上存储数据。localStorage 提供了一种持久化的本地存储方案&#xff0c;数据不会因为浏览器关闭而丢失&#xff0c;除非用户或脚本显式地删除它们。 localStorage 是一种非常实…

代码架构你想过吗?

过去的一段时间&#xff0c;我常发现我接手的服务、自己从0开始搭建起来的服务&#xff0c;整个服务代码的架构都比较混乱&#xff0c;很难去维护迭代。常见的有两种 平铺在根目录的。层次不清晰的&#xff0c;逻辑分别散落在各个层次中 我经过一段时间的分析&#xff0c;我总…

实现分布式锁的常用三种方式

分布式锁概述 我们的系统都是分布式部署的&#xff0c;日常开发中&#xff0c;秒杀下单、抢购商品等等业务场景&#xff0c;为了防⽌库存超卖&#xff0c;都需要用到分布式锁。 分布式锁其实就是&#xff0c;控制分布式系统不同进程共同访问共享资源的一种锁的实现。如果不同的…

【VUE实战项目】使用typescript重构项目

前言 本文是博主vue实战系列中的一篇&#xff0c;本系列不是专业的前端教程&#xff0c;是专门针对后端熟悉前端的。前面我们用vue实现了一个基础的管理系统&#xff0c;前文专栏地址&#xff1a; https://blog.csdn.net/joker_zjn/category_12469788.html?spm1001.2014.300…

java线程锁synchronized的几种情况

一、对象方法锁 1、成员方法加锁 同一个对象成员方法有3个synchronized修饰的方法&#xff0c;通过睡眠模拟业务操作 public class CaseOne {public synchronized void m1(){try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace()…

七款好用的公司电脑监控软件推荐|2024年电脑监控软件干货整理!

在现代企业管理中&#xff0c;电脑监控软件成为提高员工生产力、确保数据安全和防止信息泄露的重要工具。以下是七款适合公司使用的电脑监控软件推荐 固信软件https://www.gooxion.com/ 1. 固信软件 功能特点&#xff1a; 实时屏幕监控和行为分析。 网站访问历史记录和详细…

pytorch-RNN存在的问题

这里写目录标题 1. RNN存在哪些问题呢&#xff1f;1.1 梯度弥散和梯度爆炸1.2 RNN为什么会出现梯度弥散和梯度爆炸呢&#xff1f; 2. 解决梯度爆炸方法3. Gradient Clipping的实现4. 解决梯度弥散的方法 1. RNN存在哪些问题呢&#xff1f; 1.1 梯度弥散和梯度爆炸 梯度弥散是…

C++超市外卖小程序-计算机毕业设计源码62482

摘要 随着社会生活节奏加快和消费习惯的变化&#xff0c;外卖服务成为人们日常生活中不可或缺的一部分。超市外卖作为新兴业态备受关注&#xff0c;然而传统外卖平台在推荐精准度和用户体验方面存在挑战。 本研究旨在基于协同过滤算法&#xff0c;结合C语言和MySQL数据库&#…

信息化安全管理怎么做

企业如何做好安全管理工作?检查频次多了怕影响子正常生产经营&#xff0c;效益低下&#xff0c;检查频次少了又担心管控不住。”这是安全管理部综合业务室的困惑&#xff0c;也是很多企业的困惑。面对企业在安全管理中的困惑与挑战&#xff0c;采用信息化平台与精细化管理策略…

Java研学-Shiro安全框架(四)

六 SpringBoot集成Shiro认证 1 分析 Shiro提供认证授权功能&#xff0c;所以SpringBoot中不需再编写自定义注解&#xff0c;权限拦截&#xff0c;登录拦截&#xff0c;登录登出。Shiro 环境中有三个封装对象Subject &#xff0c;SecurityManager和Realms&#xff0c;SpringBoo…

hcip暑假第二次作业

ip配置如下 配置缺省路由 [R1]ip route-static 0.0.0.0 0 12.0.0.2 -------设置缺省路由 [R1]ip route-static 0.0.0.0 0 21.0.0.2 [R2]ip route-static 0.0.0.0 0 22.0.0.2 [R3]ip route-static 0.0.0.0 0 23.0.0.2 [R4]ip route-static 0.0.0.0 0 24.0.0.2 [R5]ip route…

python如何计算两个时间相差多少秒钟,分钟,小时,天,月,年

使用场景&#xff1a;在做上课记录系统的时候&#xff0c;有上课开始时间和上课结束时间&#xff0c;需要计算这两个时间的插值&#xff0c;以分钟为单位。 封装方法如下&#xff1a; from datetime import datetimedef sub_seconds(date1: str "2024-07-11 12:33:33&q…