4.是人就能学会的Spring源码教程-IOC容器创建Bean对象

news2024/11/16 21:00:44

IOC容器创建Bean对象

简单了解Bean工厂

我们要关注一个接口BeanFactory,它是Spring IOC容器的根接口,也是容器的入口。

image-20230516223651821

类的描述中已经清楚的说明了:

用于访问 Spring bean 容器的根接口。
这是 bean 容器的基本客户端视图;进一步的接口,如ListableBeanFactory和org.springframework.beans.factory.config.ConfigurableBeanFactory可用于特定目的。

我们来看一下这个接口里面的方法。

image-20230516223927341

我们可以看到有各种各样的getBean方法,让我们可以从容器中获取到各种各样的Bean对象。

BeanFactory有一个实现类DefaultListableBeanFactory我们要重点关注下。

image-20230516224734506

这个类的类图如下图所示:

image-20230516224848083

创建Bean对象

有了Bean的定义信息之后,Spring容器就可以开始创建对象了。

Bean对象的创建分为实例化初始化两个步骤。

在我们日常使用Java创建对象的过程中,可能对这两个步骤没有分得这么清楚。

实例化就是给Bean对象开辟空间进行存储,并给对象里的属性赋初始值的过程。

初始化就是给对象做一些初始化的操作,例如填充属性、执行初始化方法。接下来我们就来聊聊Bean对象的实例化和初始化的过程。

image-20230516231709467

实例化

在之前的介绍中,我们也已经知道了,Spring Bean对象的创建是通过反射的方式实现的。但是口说无凭,我们来看看代码。

@SpringBootApplication
public class SpringCodeStudyApplication {

	public static void main(String[] args) {
		SpringApplication.run(SpringCodeStudyApplication.class, args);
	}
}

我们从Spring boot程序启动的地方开始看起,上面的代码我们一定不陌生,因为这个是每一个Spring Boot程序启动的地方。我们执行这个main方法,即可启动Spring Boot应用程序。

它的底层是怎么做到的呢?

	/**
	 * Static helper that can be used to run a {@link SpringApplication} from the
	 * specified source using default settings.
	 * @param primarySource the primary source to load
	 * @param args the application arguments (usually passed from a Java main method)
	 * @return the running {@link ApplicationContext}
	 */
	public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
		return run(new Class<?>[] { primarySource }, args);
	}

	/**
	 * Static helper that can be used to run a {@link SpringApplication} from the
	 * specified sources using default settings and user supplied arguments.
	 * @param primarySources the primary sources to load
	 * @param args the application arguments (usually passed from a Java main method)
	 * @return the running {@link ApplicationContext}
	 */
	public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
		return new SpringApplication(primarySources).run(args);
	}

我们进一步点击SpringApplicationrun方法,发现run方法又调用了另外一个重载的run方法,最后调用的是SpringApplication这个类中的非静态的run方法。

我们点击进入这个非静态run方法看看。

	/**
	 * Run the Spring application, creating and refreshing a new
	 * {@link ApplicationContext}.
	 * @param args the application arguments (usually passed from a Java main method)
	 * @return a running {@link ApplicationContext}
	 */
	public ConfigurableApplicationContext run(String... args) {
		...
	}

这个run方法的注释告诉我们,这个方法运行Spring应用程序,创建并刷新一个新的ApplicationContext

这个ApplicationContext是什么?

我们去查看类图,发现了它是BeanFactory的子接口,换句话说,它就是Spring容器啊。这意味着在这个方法里将会创建并且准备好我们要使用的IOC容器。

image-20230519234749938

那我们进一步看看这个方法里做了什么?

...
context = createApplicationContext();
...
refreshContext(context);

createApplicationContext方法,从意思上我们也可以理解,这个方法创建了IOC容器。关键是refreshContext方法是做什么用的呢?

我们进一步查看这个方法。

	private void refreshContext(ConfigurableApplicationContext context) {
		if (this.registerShutdownHook) {
			shutdownHook.registerApplicationContext(context);
		}
		refresh(context);
	}
	
	/**
	 * Refresh the underlying {@link ApplicationContext}.
	 * @param applicationContext the application context to refresh
	 */
	protected void refresh(ConfigurableApplicationContext applicationContext) {
		applicationContext.refresh();
	}

我们发现refreshContext方法最后调用了容器applicationContextrefresh方法。

我们查看这个方法的注释,我们发现了,原来这个方法内部实例化了所有的单例Bean对象。

image-20230520002449825

所以我们得出了结论,refreshContext方法是对容器进行初始化操作的,至少包含了对容器内单例Bean对象的创建。

这不就是我们要找的方法吗?看来我们要找的Bean对象实例化的代码就在这个applicationContextrefresh方法里面。

我们进入refresh方法看看。

我们发现这个方法有三个实现,不过看起来,AbstractApplicationContext这个类比较像是我们要找的目标。

image-20230520002911269

进入到AbstractApplicationContext类的refresh方法,我们发现了这样的代码。

				// Instantiate all remaining (non-lazy-init) singletons.
				finishBeanFactoryInitialization(beanFactory);

注释中表示finishBeanFactoryInitialization方法会实例化单例Bean对象,说明了这个是我们的目标。

我们进入finishBeanFactoryInitialization方法看看,发现了实例化单例对象的其实调用的是Bean工厂beanFactorypreInstantiateSingletons方法。

		// Instantiate all remaining (non-lazy-init) singletons.
		beanFactory.preInstantiateSingletons();

这里的beanFactory其实指的是DefaultListableBeanFactory这个类。

进入到DefaultListableBeanFactorypreInstantiateSingletons方法,我们发现了我们距离Bean对象实例化的代码越来越近了。

		// Trigger initialization of all non-lazy singleton beans...
		for (String beanName : beanNames) {
			RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
			if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
				...
				else {
					getBean(beanName);
				}
			}
		}

这段代码告诉我们,它将会触发所有单例Bean对象的初始化。它这里做了判断,根据Bean的定义信息,也就是BeanDefinition,这个Bean必须是非抽象的,单例的,且不是懒加载的,才会被创建。

而创建的方法就是调用getBean方法,传入了Bean的名称。

我们进入getBean方法。

	@Override
	public Object getBean(String name) throws BeansException {
		return doGetBean(name, null, null, false);
	}

发现getBean方法调用了doGetBean方法。看到这个以do开头的方法,我们就要注意了,真正关键的代码就快要出现了。

进入doGetBean方法,我们发现了代码。

createBean(beanName, mbd, args);

进一步进入createBean方法,发现它的实现类在AbstractAutowireCapableBeanFactory上,它是AbstractBeanFactory的子类。

image-20230520004646478

createBean方法中,我们发现了如下代码

		try {
			Object beanInstance = doCreateBean(beanName, mbdToUse, args);
			if (logger.isTraceEnabled()) {
				logger.trace("Finished creating instance of bean '" + beanName + "'");
			}
			return beanInstance;
		}

又是一个do开头的方法,我们知道,我们又得打起精神了,这里就是创建Bean对象的实际操作方法。

进一步进入doCreateBean方法。

		if (instanceWrapper == null) {
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}

我们发现了createBeanInstance的方法,心态要崩了呀,Spring的源码怎么嵌套地这么深,要看一个实现咋就这么难。让我们打起精神,胜利就在前方,就快要找到创建Bean对象的方法了。

我们进入createBeanInstance方法,看到了如下代码。

		// No special handling: simply use no-arg constructor.
		return instantiateBean(beanName, mbd);

这段代码表示使用无参的构造器,我觉得我们就快要找到反射创建Bean对象的代码了。

进入instantiateBean方法,我们看到了代码。

beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, this);

我的天啊,胜利的曙光就要来了。这个代码显示获取实例化策略来实例化代码。

我们进入instantiate方法,我们发现进入的类是SimpleInstantiationStrategy

	@Override
	public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
		// Don't override the class with CGLIB if no overrides.
		if (!bd.hasMethodOverrides()) {
			Constructor<?> constructorToUse;
			synchronized (bd.constructorArgumentLock) {
				constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
				if (constructorToUse == null) {
					final Class<?> clazz = bd.getBeanClass();
					if (clazz.isInterface()) {
						throw new BeanInstantiationException(clazz, "Specified class is an interface");
					}
					try {
						if (System.getSecurityManager() != null) {
							constructorToUse = AccessController.doPrivileged(
									(PrivilegedExceptionAction<Constructor<?>>) clazz::getDeclaredConstructor);
						}
						else {
							constructorToUse = clazz.getDeclaredConstructor();
						}
						bd.resolvedConstructorOrFactoryMethod = constructorToUse;
					}
					catch (Throwable ex) {
						throw new BeanInstantiationException(clazz, "No default constructor found", ex);
					}
				}
			}
			return BeanUtils.instantiateClass(constructorToUse);
		}
		else {
			// Must generate CGLIB subclass.
			return instantiateWithMethodInjection(bd, beanName, owner);
		}
	}

在这代码里,我们发现了什么?

Constructor<?>类型,终于出现了反射部分的代码。这个方法先获取到了Bean对象实例化要用到的构造器对象,再调用BeanUtils.instantiateClass方法进行实例化。

我们继续进入BeanUtils.instantiateClass方法。

	public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException {
		...
				return ctor.newInstance(argsWithDefaultValues);
		...
	}

终于,我们要找到的反射代码终于出现了。

Spring通过获取到Bean对象的构造器,并调用了newInstance方法实例化了对象。

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

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

相关文章

小牛电动疲软的销售趋势和不明朗的未来

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 小牛电动(NIU)的股价今年迄今为止已经下跌了6%。尽管该公司在蓬勃发展的电动汽车市场运营&#xff0c;但疫情的限制和欧洲的销售疲软导致小牛电动在欧市场的销量持续下降&#xff0c;猛兽财经认为&#xff0c;由于规模经济…

【LeetCode】528. 按权重随机选择

528. 按权重随机选择&#xff08;中等&#xff09; 思路 我们先把题目读懂。假设我们有数组 w[1,2,3,4], 那么这个数组的的和为 123 4 10 。我们得到 index (0,1,2,3) 的概率为 [1/10,2/10,3/10,4/10]。现在我们要返回(0,1,2,3) 中任意一个 index&#xff0c;但是我们要保证 p…

Docker安装MeiliSearch教程

MeiliSearch是一款开源的全文搜索引擎&#xff0c;它使用Rust编写&#xff0c;具有高效、快速、易用的特点。MeiliSearch支持多种语言&#xff0c;可以轻松地集成到任何应用程序中。它可以处理大量的文本数据&#xff0c;并能够快速地搜索和返回相关的结果。MeiliSearch还提供了…

[深度学习]大模型训练之框架篇-DeepSpeed

一 为什么需要Deepspeed 大模型&#xff08;LLM&#xff09;在训练时往往需要大量内存来存储中间激活、权重等参数&#xff0c;百亿模型甚至无法在单个 GPU上进行训练&#xff0c;使得模型训练在某些情况下非常低效和不可能。这就需要进行多卡&#xff0c;或者多节点分布式训练…

zabbix企业监控实战-1-zabbix部署

官网&#xff1a;https://www.zabbix.com 参考官网&#xff1a;https://www.zabbix.com/download?zabbix6.0&os_distributionrocky_linux&os_version8&componentsserver_frontend_agent&dbmysql&wsapache 1、环境准备 1> 修改主机名 [rootzabbix ~]# h…

Java --- redis7实现分布式锁

目录 一、锁的种类 二、分布式锁具备的条件与刚需 三、springbootredisngnix单机实现案例 四、Nginx配置负载均衡 4.1、修改nginx配置文件 4.2、执行启动命令 4.3、启动微服务程序测试 五、使用redis分布式锁 5.1、方法递归重试 5.2、自旋方式 5.3、添加key过期时间&#xff0…

业务实战记录4:多维表插入数据任务丢失处理与思考

本文目录 一、线程 bug二、解决方案2.1 加停顿2.2 单线程2.3 多 Token 三、一点花絮 很久没有写业务实战记录了&#xff0c;实际工作过程中其实遇到了挺多问题的&#xff0c;但是要通过 CSDN 记录下来&#xff0c;还是比较难的&#xff0c;因为场景和目标比较难说清楚&#xff…

【网络】HTTPHTTPS协议

文章目录 HTTP协议认识URLurlencode和urldecodeHTTP协议格式HTTP请求协议格式简单的小实验 HTTP响应协议格式关于封装解包分用 HTTP的方法关于GET和POST方法概念GET&POST对比(代码测试)测试POST和GET方法的区别 HTTP的状态码关于重定向的状态码临时重定向的代码演示: HTTP的…

【文献研究】轴辐式航线网络设计—Liner hub-and-spoke shipping network design

学习文献&#xff1a;轴辐式航线网络设计—Liner hub-and-spoke shipping network design 3. 模型建立 轴辐式航线网络设计 三级轴辐式网络&#xff1a;喂给港-二级枢纽港-一级枢纽港 主要考虑的限制条件&#xff1a;多种类型的集装箱船舶、转运时间、多种类型的集装箱 转运操…

Kangas:计算机视觉中的Pandas

介绍 在计算机视觉领域&#xff0c;Kangas是一种越来越受欢迎的工具&#xff0c;用于图像数据处理和分析。类似于Pandas如何改变数据分析人员处理表格数据的方式&#xff0c;Kangas对计算机视觉任务也起到了同样的作用。 Kangas是Comet ML开源的工具&#xff0c;用于探索、分析…

【Http协议①】认识http协议,学会使用fiddler抓包工具进行抓包.

前言: 大家好,我是良辰丫,今天我们一起来学习http协议,http协议是应用层的协议,应用层是最接近程序员的,那么,http协议到底是什么呢?我们往下看.&#x1f49e;&#x1f49e; &#x1f9d1;个人主页&#xff1a;良辰针不戳 &#x1f4d6;所属专栏&#xff1a;javaEE初阶 &#…

springboot+java养老院儿童福利院管理系统

安家儿童福利院管理系统包括儿童管理、申请领养管理、捐赠管理、楼栋管理、宿舍管理、分配信息管理、宿舍物品管理、报修管理、维修工管理、报修状态管理、留言管理、系统管理。通过对系统的实现得出安家儿童福利院管理系统具有安全稳定、操作简单、功能强大等特点&#xff0c;…

MySQL8.0卸载、安装和使用(二)

MySQL数据库的安装 注意&#xff1a; 必须用系统管理员身份运行mysql安装程序。安装目录切记不要用中文。 步骤一&#xff1a;双击mysql8的安装向导 步骤二&#xff1a;分为首次安装和再安装 1、首次安装 &#xff08;1&#xff09;如果是首次安装mysql系列的产品&#xff…

第06章_多表查询

第06章_多表查询 多表查询&#xff0c;也称为关联查询&#xff0c;指两个或更多个表一起完成查询操作。 前提条件&#xff1a;这些一起查询的表之间是有关系的&#xff08;一对一、一对多&#xff09;&#xff0c;它们之间一定是有关联字段&#xff0c;这个关联字段可能建立了…

脑科学研究者的案头书(含下载资源)

脑科学研究者的案头书 <<< 回复关键词获取下载链接 >>> 《EEG Signal Processing and Machine Learning》&#xff08;Second Edition&#xff09; 简介&#xff1a; 《脑电信号处理与机器学习》书籍旨在描述脑电图(EEG)研究中的新技术和成果&#xff0c;主…

chatgpt赋能Python-python3捕获异常

Python3异常处理技术详解 在Python3中&#xff0c;异常处理技术是一项非常重要的工具。它能够帮助程序员避免不可预见的错误&#xff0c;减少不必要的程序崩溃&#xff0c;保证程序的稳定性。 什么是异常&#xff1f; 异常就是程序在执行过程中发生的错误或异常情况。不同的…

Redis+LUA脚本实现限流

文章目录 1、demo结构2、自定义接口3、编写写LUA脚本4、通过AOP切面识别需要限流的接口编写切面AOP通知类型 5、Redis限流自定义异常构建Redis限流自定义异常声明这个类为全局异常处理器专属日志 6、流量限制器RateLimiterRateLimitAlgApiLimitRateLimitRuleRuleConfig 7、Guav…

Win11系统不兼容怎么回退到Win10系统使用?

Win11系统不兼容怎么回退到Win10系统使用&#xff1f;有用户将自己的电脑系统升级到了Win11之后&#xff0c;发现使用起来非常的卡顿&#xff0c;自己的电脑配置不足。那么这个情况怎么去进行问题的解决呢&#xff1f;来看看以下详细的解决方法分享吧。 准备工作&#xff1a; 1…

Golang每日一练(leetDay0071) 同构字符串、反转链表

目录 205. 同构字符串 Isomorphic Strings &#x1f31f; 206. 反转链表 Reverse Linked-list &#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Rust每日一练 专栏 Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练 专栏 Java每日一练 专栏 205. 同…

Debezium系列之:Debezium镜像仓库Quay.io,使用Debezium镜像仓库的方法和案例

Debezium系列之:Debezium镜像仓库Quay.io,使用Debezium镜像仓库的方法和案例 一、Debezium镜像仓库变动二、镜像仓库[Quay.io](https://quay.io/organization/debezium)三、使用镜像仓库Quay.io方法四、使用镜像仓库下载Debezium UI一、Debezium镜像仓库变动 Debezium2.2版本…