Spring源码十五:Bean的加载

上一篇我们通过Spring源码十四:Spring生命周期介绍了refresh的最后两个方法,至此通过前面大概十篇左右的篇幅介绍完了Spring容器初始化,接下来,将进入Spring另外一个模块Bean相关的知识点。

在Spring框架中,Bean加载过程是一个复杂且至关重要的环节。在这一节的内容中,我们将探讨如何从BeanDefinition实例化一个Bean,并了解Spring在ApplicationContext初始化时的一些高级特性在Bean加载过程中如何发挥作用。下面是我们的分析步骤:


回到我们最开始的示例代码如下:

package org.springframework;

import org.springframework.ApplicationContext.MyselfClassPathXmlApplicationContext;
import org.springframework.context.ApplicationContext;
import org.springframework.dto.JmUser;
import org.springframework.event.MyselfEvent;

/**
 * @author Jeremy
 * @version 1.0
 * @description: 主方法
 * @date 2024/6/30 02:58
 */
public class Main {
	public static void main(String[] args) {
		ApplicationContext context = new MyselfClassPathXmlApplicationContext("applicationContext.xml");
		JmUser jmUser = (JmUser)context.getBean("jmUser");
//		System.out.println(jmUser.getName());
//		System.out.println(jmUser.getAge());
		// 发布事件
		MyselfEvent event = new MyselfEvent("事件源:source", "This is a custom event");
		context.publishEvent(event);


	}
}

到目前我们为止,咱们知识跟踪了:

ApplicationContext context = new MyselfClassPathXmlApplicationContext("applicationContext.xml");

这一段代码接下来我们看下面的方法:


getBean

判断Spring容器是否激活和关闭,如果测试容器还未激活或者已经关闭则直接抛出异常。毕竟Spring中的bean都是存在容器中的,容器都没有了到哪儿获取bean呢?

接着往下看,通过getBeanFactory先获取到容器,返回的是DefaultListableBeanFactory,前面有分析过它的类图,这里就展开了,在执行的getBeanFactory方法的时候,我特意将BeanDefinitionNames属性展开了,可以这个名字很熟悉。在给大家看下我们的xml配置,这下是不是就很熟悉了呢:

铺垫了这么多就是为了让大家将前面的内容给串联起来,我们在Spring中的每个配置最终都会转化成Spring容器中的属性。好了接下来我们开始正式进入getBean方法。


看到我们进入了BeanFactory的getBean方法,这个时候回去调用doGetBean方法。在Spring中但凡看到do作为前缀的一定要留一个心眼,基本上是真正处理逻辑的地方。

进入doGetBean方法一探究竟:


doGetBean

/**
	 * Return an instance, which may be shared or independent, of the specified bean.
	 * @param name the name of the bean to retrieve
	 * @param requiredType the required type of the bean to retrieve
	 * @param args arguments to use when creating a bean instance using explicit arguments
	 * (only applied when creating a new instance as opposed to retrieving an existing one)
	 * @param typeCheckOnly whether the instance is obtained for a type check,
	 * not for actual use
	 * @return an instance of the bean
	 * @throws BeansException if the bean could not be created
	 */
	@SuppressWarnings("unchecked")
	protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
			@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

		// 获取转换后的Bean名称
		final String beanName = transformedBeanName(name);
		Object bean;

		// Eagerly check singleton cache for manually registered singletons.
		// 检查单例缓存中是否有手动注册的单例Bean
		Object sharedInstance = getSingleton(beanName);
		// 如果单缓存中存在则直接从缓存中获取,
		// 如果不存在就走else分支,进行实例化
		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 + "'");
				}
			}
			// 获取Bean的实例对象
			bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
		}

		else {
			// 如果当前bean还未被实例化,则在这个判断中准备实例化
			// Fail if we're already creating this bean instance:
			// We're assumably within a circular reference.
			// 如果bean的类型是prototype且正在创建,直接抛出异常
			if (isPrototypeCurrentlyInCreation(beanName)) {
				throw new BeanCurrentlyInCreationException(beanName);
			}

			// Check if bean definition exists in this factory.
			// 检查Bean定义是否存在于当前工厂
			/// 获取容器的父容器
			BeanFactory parentBeanFactory = getParentBeanFactory();
			// 存在父容器,且当前容器没有beanName的BeanDefinition,则通过父容器获取bean
			if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
				// Not found -> check parent.
				String nameToLookup = originalBeanName(name);
				if (parentBeanFactory instanceof AbstractBeanFactory) {
					return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
							nameToLookup, requiredType, args, typeCheckOnly);
				}
				else if (args != null) {
					// Delegation to parent with explicit args.
					return (T) parentBeanFactory.getBean(nameToLookup, args);
				}
				else if (requiredType != null) {
					// No args -> delegate to standard getBean method.
					return parentBeanFactory.getBean(nameToLookup, requiredType);
				}
				else {
					return (T) parentBeanFactory.getBean(nameToLookup);
				}
			}

			if (!typeCheckOnly) {
				// 标记bean已经开始创建,其实就是将当前beanName 放入alreadyCreated容器中
				markBeanAsCreated(beanName);
			}

			// 进入实例化bean阶段
			try {
				// 合并容器中beanName对应的BeanDefinition,得到新的root类型BeanDefinition
				final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
				// 校验合并后的rootBeanDefinition是否达到实例化标准,abstractFlag默认是false,如果是true则抛出异常
				checkMergedBeanDefinition(mbd, beanName, args);

				// Guarantee initialization of beans that the current bean depends on.
				// 获取当前beanName所依赖的BeanNames数组
				String[] dependsOn = mbd.getDependsOn();
				// 如果依赖的bean不为空,则注册这些bean且递归调用getBean,提前实例化需要依赖的Bean
				if (dependsOn != null) {
					for (String dep : dependsOn) {
						if (isDependent(beanName, dep)) {
							throw new BeanCreationException(mbd.getResourceDescription(), beanName,
									"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
						}
						// 注册依赖的bean
						registerDependentBean(dep, beanName);
						try {
							// 递归调用getBean,需要依赖的Bean先实例化
							getBean(dep);
						}
						catch (NoSuchBeanDefinitionException ex) {
							throw new BeanCreationException(mbd.getResourceDescription(), beanName,
									"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
						}
					}
				}

				// Create bean instance.
				// 判断beanDefinition是单例?
				if (mbd.isSingleton()) {
					sharedInstance = getSingleton(beanName, () -> {
						try {
							//是单例:则开始创建单例bean的实例
							return createBean(beanName, mbd, args);
						}
						catch (BeansException ex) {
							// Explicitly remove instance from singleton cache: It might have been put there
							// eagerly by the creation process, to allow for circular reference resolution.
							// Also remove any beans that received a temporary reference to the bean.
							destroySingleton(beanName);
							throw ex;
						}
					});
					// 获取bean实例对象
					bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}

				// 如果是原型bean
				else if (mbd.isPrototype()) {
					// It's a prototype -> create a new instance.
					Object prototypeInstance = null;
					try {
						// 实例化前置准备工作
						beforePrototypeCreation(beanName);
						// 开始实例化
						prototypeInstance = createBean(beanName, mbd, args);
					}
					finally {
						// 实例化以后的处理工作
						afterPrototypeCreation(beanName);
					}
					// 获取原型bean实例对象
					bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
				}

				// 其他bean类型处理
				else {
					String scopeName = mbd.getScope();
					final Scope scope = this.scopes.get(scopeName);
					// 空抛出异常
					if (scope == null) {
						throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
					}
					try {
						// 处理流程类似原型
						Object scopedInstance = scope.get(beanName, () -> {
							// 创建其他类型作用域bean的前置准备工作
							beforePrototypeCreation(beanName);
							try {
								// 创建其他类型作用域bean
								return createBean(beanName, mbd, args);
							}
							finally {
								// 后置处理作用
								afterPrototypeCreation(beanName);
							}
						});
						// 获取其他作用域类型实例化对象
						bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
					}
					catch (IllegalStateException ex) {
						throw new BeanCreationException(beanName,
								"Scope '" + scopeName + "' is not active for the current thread; consider " +
								"defining a scoped proxy for this bean if you intend to refer to it from a singleton",
								ex);
					}
				}
			}
			catch (BeansException ex) {
				cleanupAfterBeanCreationFailure(beanName);
				throw ex;
			}
		}

		// Check if required type matches the type of the actual bean instance.
		// 如果requiredType,且bean类型与入参要求不一致,则需要惊喜转换
		if (requiredType != null && !requiredType.isInstance(bean)) {
			try {
				// 将BeanDefinition转化成指定类型的bean
				T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
				if (convertedBean == null) {
					throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
				}
				return convertedBean;
			}
			catch (TypeMismatchException ex) {
				if (logger.isTraceEnabled()) {
					logger.trace("Failed to convert bean '" + name + "' to required type '" +
							ClassUtils.getQualifiedName(requiredType) + "'", ex);
				}
				throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
			}
		}
		return (T) bean;
	}

在Spring框架中,doGetBean方法是获取Bean实例的核心方法。这个方法涉及很多步骤,包括从缓存中获取单例Bean、处理原型Bean的创建、检查Bean定义、处理循环依赖等。下面我们会想分析refresh方法一样,先整体看下大概内容,再细节到一个一个方法进行解读。

流程总结

  1. 获取转换后的Bean名称:
    ○ 通过transformedBeanName方法获取转换后的Bean名称。
  2. 单例缓存检查:
    ○ 检查单例缓存中是否有手动注册的单例Bean。如果存在并且args为空,则直接从缓存中获取该Bean实例。
    ○ 如果在缓存中找到了Bean实例,并且args为空,则获取该Bean的实际对象并返回。
  3. Bean创建:
    ○ 如果单例缓存中没有找到该Bean实例,或者args不为空,则准备创建该Bean。
    ○ 如果当前Bean是Prototype且正在创建,则抛出异常,避免循环依赖。
    ○ 检查当前Bean的定义是否存在于当前工厂中,如果不存在则向父工厂请求该Bean实例。
  4. 标记Bean创建状态:
    ○ 如果typeCheckOnly为false,则将该Bean标记为已经开始创建。
  5. 合并Bean定义:
    ○ 合并容器中的Bean定义,得到新的Root类型的Bean定义。
  6. 校验合并后的Bean定义:
    ○ 校验合并后的Bean定义是否符合实例化要求,例如是否是抽象Bean。
  7. 处理依赖关系:
    ○ 获取当前Bean依赖的Bean数组,并递归调用getBean方法,提前实例化需要依赖的Bean。
  8. 实例化Bean:
    ○ 根据Bean的作用域(单例、原型或其他)进行实例化。
    ○ 对于单例Bean,获取单例缓存或创建新的单例实例。
    ○ 对于原型Bean,创建新的原型实例。
    ○ 对于其他作用域的Bean,通过作用域获取Bean实例。
  9. 类型转换:
    ○ 如果requiredType不为空且Bean实例的类型与requiredType不一致,则进行类型转换。

doGetBean方法是Spring框架中Bean创建与获取的核心逻辑,实现了复杂的Bean生命周期管理。通过单例缓存合并Bean定义、处理依赖关系以及类型转换等步骤,确保了Bean实例的正确创建与获取。此流程通过细致的检查与处理,避免了循环依赖,并支持多种作用域的Bean管理,为应用提供了灵活且可靠的Bean管理机制。

总结

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

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

相关文章

人工智能时代打工人摸鱼秘籍(1)- 为啥说大模型像人?

人工智能以势不可挡的方式席卷全球。 所有公司&#xff0c;都在削尖脑袋想&#xff0c;如何在在产品、营销、运营、服务和管理上加持大人工智能的能力。 公司在卷生卷死的时候&#xff0c;有一批人已经偷偷在用大模型提&#xff08;摸&#xff09;效&#xff08;鱼&#xff09;…

从打印到监测:纳米生物墨水助力3D生物打印与组织监测平台?

从打印到监测&#xff1a;纳米生物墨水助力3D生物打印与组织监测平台&#xff1f; 在 3D 组织工程中&#xff0c;纳米生物墨水是将纳米材料与 ECM 水凝胶结合&#xff0c;以提高其打印性和功能性的重要策略。纳米生物墨水可以增强水凝胶的机械性能、导电性、生物活性&#xff…

2024高考作文题“人工智能”

今年开年到现在&#xff0c;明显的感受就是&#xff0c;咨询人工智能机器人的客户比往年更多了。什么原因&#xff0c;是因为人工成本太高了&#xff0c;今年整体经济环境变差&#xff0c;招不起人&#xff0c;所以想用AI机器人来降低用工成本吗&#xff1f; 还是说因为语音线路…

JVM专题之G1垃圾收集器下

索引(记录)的源码的工作流程图如下: CSet(Collection Set 回收集合) 收集集合(CSet)代表每次GC暂停时回收的一系列目标分区。在任意一次收集暂停中,CSet所有分区都会被释放,内部存活的对象都会被转移到分配的空闲分区中。因此无论是年轻代收集,还是混合收集,工作的机…

PsQuerySystemDllInfo逆向

typedef struct _SYSTEM_DLL_ENTRY {ULONG64 type;UNICODE_STRING FullName;PVOID ImageBase;PWCHAR BaseName;PWCHAR StaticUnicodeBuffer; }SYSTEM_DLL_ENTRY, * PSYSTEM_DLL_ENTRY; 返回值为上面的结构体指针 验证 type: fullname inagebase: pwchar basename PWCHAR …

Spring源码十六:Bean名称转化

在上一篇Spring源码十五&#xff1a;Bean的加载 中我们通过前面的案例方法&#xff0c;找到了Spring真正开始创建Bean的入口&#xff0c;也就是doGetBean方法。该方法是Spring框架中Bean创建与获取的核心逻辑&#xff0c;实现了复杂的Bean生命周期管理。通过单例缓存、合并Bean…

NLP简介

自然语言处理( Natural Language Processing, NLP)是计算机科学领域与人工智能领域中的一个重要方向。它研究能实现人与计算机之间用自然语言进行有效通信的各种理论和方法。自然语言处理是一门融语言学、计算机科学、数学于一体的科学。因此&#xff0c;这一领域的研究将涉及自…

保存在FinalShell服务器登录密码忘记了,如何快速获取到

一、从FinalShell获取服务器基本信息 如图操作会导出一个json文件&#xff0c;可以直接保存在桌面&#xff0c;或者其他位置 json格式如下&#xff1a; {"forwarding_auto_reconnect":false ,"custom_size":false ,"delete_time":0 ,"sec…

从0到1制作单只鳌虾运动轨迹追踪软件

前言 需要准备windows10操作系统&#xff0c;python3.11.9&#xff0c;cuDNN8.9.2.26&#xff0c;CUDA11.8&#xff0c;paddleDetection2.7 流程&#xff1a; 准备数据集-澳洲鳌虾VOC数据集 基于RT-DETR目标检测模型训练导出onnx模型进行python部署平滑滤波处理视频帧保留的…

数字化精益生产系统--QMS质量管理系统

QMS质量管理系统&#xff08;Quality Management System&#xff09;是现代企业管理的关键组成部分&#xff0c;旨在确保产品和服务的质量达到或超过客户需求和期望。 以下是对QMS质量管理系统的功能设计&#xff1a;

ip地址突然变了一个城市怎么办

在数字化日益深入的今天&#xff0c;IP地址不仅是网络连接的标识&#xff0c;更是我们网络行为的“身份证”。然而&#xff0c;当您突然发现您的IP地址从一个城市跳转到另一个城市时&#xff0c;这可能会引发一系列的疑问和担忧。本文将带您深入了解IP地址突变的可能原因&#…

软件系统架构的一些常见专业术语

分层架构是逻辑上的&#xff0c;在物理部署上&#xff0c;三层结构可以部署在同一个物理机器上&#xff0c;但是随着网站业务的发展&#xff0c;必然需要对已经分层的模块分离部署&#xff0c;即三层结构分别部署在不同的服务器上&#xff0c;使网站拥有更多的计算资源以应对越…

信号与系统笔记分享

文章目录 一、导论信号分类周期问题能量信号和功率信号系统的线性判断时变&#xff0c;时不变系统因果系统判断记忆性系统判断稳定性系统判断 二、信号时域分析阶跃函数冲激函数取样性质四种特性1 筛选特性2 抽样特性3 展缩特性4 卷积特性卷积作用 冲激偶函数奇函数性质公式推导…

Java版Flink使用指南——安装Flink和使用IntelliJ制作任务包

大纲 安装Flink操作系统安装JDK安装Flink修改配置启动Flink测试 使用IntelliJ制作任务包新建工程Archetype 编写测试代码打包测试 参考资料 在《0基础学习PyFlink》专题中&#xff0c;我们熟悉了Flink的相关知识以及Python编码方案。这个系列我们将使用相对主流的Java语言&…

C++基础(十一):STL简介

从今天开始&#xff0c;我们正式步入STL的学习&#xff0c;STL&#xff08;标准模板库&#xff0c;Standard Template Library&#xff09;是C标准库的重要组成部分&#xff0c;提供了一系列通用的类和函数模板&#xff0c;包括容器、算法、迭代器等。它的设计极大地提高了代码…

中国科学技术大学发布了2024年少年班录取名单

7月7日&#xff0c;中国科学技术大学发布了2024年少年班录取名单公示&#xff0c;来自上海的12岁“小孩哥”刘尧进入名单。 据澎湃新闻此前报道&#xff0c;刘尧是因为此前通过了中科大少年班的校测考试&#xff0c;提前拿到了“高考体验券”。他所在的上海市实验学校&#xff…

柳叶刀:5Kg负重巡飞无人机技术详解

一、引言 随着无人机技术的不断发展&#xff0c;巡飞无人机在军事侦察、环境监测、边境巡逻等领域的应用日益广泛。其中&#xff0c;“柳叶刀”作为一款5Kg负重巡飞无人机&#xff0c;凭借其独特的机体结构、高效的动力系统、先进的飞行控制系统等技术优势&#xff0c;在众多无…

【位运算】基础算法总结

目录 基础位运算给一个数n&#xff0c;确定它的二进制表示的第x位是0还是1将一个数n的二进制表示的第x位修改成1将一个数n的二进制表示的第x位修改成0位图思想&#xff08;哈希表&#xff09;提取一个数&#xff08;n&#xff09;二进制表示中的最右侧的1&#xff08;lowbit&am…

KIVY 3D Rotating Monkey Head¶

7 Python Kivy Projects (With Full Tutorials) – Pythonista Planet KIVY 3D Rotating Monkey Head kivy 3D 旋转猴子头How to display rotating monkey example in a given layout. Issue #6688 kivy/kivy GitHub 3d 模型下载链接 P99 - Download Free 3D model by …

vue学习笔记(购物车小案例)

用一个简单的购物车demo来回顾一下其中需要注意的细节。 先看一下最终效果 功能&#xff1a; &#xff08;1&#xff09;全选按钮和下面的商品项的选中状态同步&#xff0c;当下面的商品全部选中时&#xff0c;全选勾选&#xff0c;反之&#xff0c;则不勾选。 &#xff08…