Spring源码十六:Bean名称转化

news2024/10/6 8:38:32

在上一篇Spring源码十五:Bean的加载 中我们通过前面的案例方法,找到了Spring真正开始创建Bean的入口,也就是doGetBean方法。该方法是Spring框架中Bean创建与获取的核心逻辑,实现了复杂的Bean生命周期管理。通过单例缓存、合并Bean定义、处理依赖关系以及类型转换等步骤,确保了Bean实例的正确创建与获取。此流程通过细致的检查与处理,避免了循环依赖,并支持多种作用域的Bean管理,为应用提供了灵活且可靠的Bean管理机制。上一篇,我们只是从整体上看了该方法。

今天开始正式进入该方法的详解,接下来让我看它的第一个方法:


transformedBeanName 

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

看到上述代码我们方法,在调用canonicalName的时候,是通过BeanFactroyUtils的transformerBeanName方法先进行了处理,然后再作为canonicalName的参数进行命名的规范。直接进入方法查看:

/**
	 * Return the actual bean name, stripping out the factory dereference
	 * prefix (if any, also stripping repeated factory prefixes if found).
	 * 
	 * 去掉 Bean 名称中的所有 & 前缀,并返回实际的 Bean 名称。
	 * 
	 * @param name the name of the bean
	 * @return the transformed name
	 * @see BeanFactory#FACTORY_BEAN_PREFIX
	 */
	public static String transformedBeanName(String name) {
		Assert.notNull(name, "'name' must not be null");
		// bean的名称不是以&为前缀的,不处理直接返回
		if (!name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) { // 	String FACTORY_BEAN_PREFIX = "&";
			return name;
		}
		// 如果name以一个或多个符号“&”为前缀,
		// 则遍历剔除掉name前缀中所有的“&”,返回name。
		return transformedBeanNameCache.computeIfAbsent(name, beanName -> {
			do {
				beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
			}
			while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
			return beanName;
		});
	}

在这段代码中:

  1. FACTORY_PREFIX 常量表示工厂引用前缀(&)。
  2. getActualBeanName 方法接收一个 Bean 名称作为输入,并去除所有的工厂前缀。
  3. while 循环检查 Bean 名称是否以工厂前缀开头。如果是,则使用 substring 方法去除前缀,并继续循环直到去除所有前缀。

这个方法会去掉 Bean 名称中的所有 & 前缀,并返回实际的 Bean 名称。至于为什么这么做如果看过详细看过Bean生命周期代码的小伙伴应该知道,咱们先不解读,整个bean相关分析结束就知道啦。那咱们接着往下看:


canonicalName

在 Spring 框架中,bean 的别名(alias)是一种灵活的机制,它允许开发者为一个 bean 定义多个名称。在实际使用中,用户可能通过别名访问某个 bean,因此需要一个过程将别名解析为实际名称,这样 Spring 才能找到对应的 BeanDefinition

  1. 别名注册

    • BeanDefinition 注册到 Spring 容器时,可以为一个 bean 定义多个别名。
    • 这些别名与实际名称的映射关系存储在 aliasMap 中。
  2. 别名解析

    • 当用户调用 getBean 方法时,传入的名称可能是别名。
    • canonicalName 方法通过查找 aliasMap,将别名解析为实际名称。
    • 解析完成后,Spring 使用实际名称查找并实例化对应的 BeanDefinition

小结

初步分析了bean的名称解析并转换相关的代码逻辑,从下一篇开始,我们就要开始bean加载的核心逻辑分析了。

总结

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

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

相关文章

文章解读与仿真程序复现思路——太阳能学报EI\CSCD\北大核心《计及电-热-氢负荷与动态重构的主动配电网优化调度》

本专栏栏目提供文章与程序复现思路&#xff0c;具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源…

【双一流高校主办,Springer-LNICST出版,EI稳定检索】2024年应用计算智能、信息学与大数据国际会议(ACIIBD 2024,7月26-28)

2024年应用计算智能、信息学与大数据国际学术会议&#xff08;ACIIBD 2024&#xff09;将于2024年7月26-28日在中国广州举办。会议将聚焦于计算智能及其应用、信息、大数据等相关的研究领域&#xff0c; 广泛邀请国内外知名专家学者&#xff0c;共同探讨相关学科领域的最新发展…

Maya崩溃闪退常见原因及解决方案

Autodesk Maya 是一款功能强大的 3D 计算机图形程序&#xff0c;被电影、游戏和建筑等各个领域的设计师广泛使用。然而&#xff0c;Maya 就像任何其他软件一样可能会发生崩溃问题。在前文中&#xff0c;小编给大家介绍了3ds Max使用V-Ray渲染时的崩溃闪退解决方案&#xff1a; …

电路基础知识汇总

1.0 串连&#xff0c;并联&#xff0c;混连 串联的定义 电路串联是一种电路元件的连接方式&#xff0c;其中各个元件沿着单一路径互相连接&#xff0c;形成一个连续的链。在串联电路中&#xff0c;每个节点最多只连接两个元件&#xff0c;这意味着电流只有一条路径可以通过整个…

“来来来,借一步说话”,让前端抓狂的可视化大屏界面。

可视化大屏的前端开发难度要远远高于普通前端&#xff0c;尤其是当设计师搞出一些花哨的效果&#xff0c;很容易让UI和前端陷入口水大战中。 可视化大屏的前端开发相比普通前端开发的难度要高&#xff0c;主要是因为以下几个方面&#xff1a; 1. 数据量大&#xff1a; 可视化…

01:spring

文章目录 一&#xff1a;常见面试题1&#xff1a;什么是Spring框架&#xff1f;1.1&#xff1a;spring官网中文1.2&#xff1a;spring官网英文 2&#xff1a;谈谈自己对于Spring IOC和AOP的理解2.1&#xff1a;IOCSpring Bean 的生命周期主要包括以下步骤&#xff1a; 2.2&…

STM32-输入捕获IC和编码器接口

本内容基于江协科技STM32视频学习之后整理而得。 文章目录 1. 输入捕获IC1.1 输入捕获IC简介1.2 频率测量1.3 输入捕获通道1.4 主从触发模式1.5 输入捕获基本结构1.6 PWMI基本结构 2. 输入捕获库函数及代码2.1 输入捕获库函数2.2 6-6 输入捕获模式测频率2.2.1 硬件连接2.2.2 硬…

DevEco Studio无法识别本地模拟器设备的解决方法

目录 场景 解决办法 方式1 方式2 场景 有很多小伙伴遇到过安装了手机模拟器, 但是开发工具设备栏不识别手机设备的问题, 如下图,明明模拟器都安装了,并启动, 但为什么设备栏不显示呢? 解决后的截图,应该是这样(其实跟 android 类似 )

拓扑排序,PageRank(markov),实对称矩阵等

拓扑排序 多件事情有先后顺序&#xff0c;如何判断哪个先哪个后 拓扑排序算法&#xff1a; 1.读入图时&#xff0c;需要记录每个顶点的入度&#xff0c;以及相邻的所有顶点 2.将入度为0的顶点入队&#xff08;先进先出&#xff09; 3.取出队首元素a&#xff0c;&#xf…

检测到弱密码:并非所有密码套件均支持完全前向保密解决方案

问题 检测到弱密码&#xff1a;并非所有密码套件均支持完全前向保密&#xff08;弱密码套件 - ROBOT 攻击&#xff1a;服务器支持易受攻击的密码套件&#xff09; 背景介绍 HTTP 协议自身没有加密机制&#xff0c;但可以通过与 TLS (Transport Layer Security) / SSL (Secur…

QThread moveToThread的妙用

官方文档描述 总结就是移动到线程的对象不能有父对象&#xff0c;执行start即起一个线程&#xff0c;示例是将myObject移动到主线程中。QT中这种方式起一个线程是非常简单的。 示例描述以及代码 描述往Communicate线程中频繁添加任务&#xff0c;等任务结束的时候统计计算的结…

用C#调用Windows API向指定窗口发送按键消息详解与示例

文章目录 1. 按键消息的定义及功能2. 引入所需的命名空间3. 定义Windows API函数4. 定义发送消息的方法5. 获取窗口句柄6. 调用API发送按键消息7. 使用示例注意事项总结 在C#中调用Windows API向指定窗口发送按键消息是一种常见的操作&#xff0c;这通常用于自动化脚本、游戏辅…

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

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

Vue3+.NET6前后端分离式管理后台实战(二十九)

1&#xff0c;Vue3.NET6前后端分离式管理后台实战(二十九)

go语言Gin框架的学习路线(六)

gin的路由器 Gin 是一个用 Go (Golang) 编写的 Web 框架&#xff0c;以其高性能和快速路由能力而闻名。在 Gin 中&#xff0c;路由器是框架的核心组件之一&#xff0c;负责处理 HTTP 请求并将其映射到相应的处理函数上。 以下是 Gin 路由器的一些关键特性和工作原理的简要解释…

【Unity2D 2022:Particle System】添加拾取粒子特效

一、创建粒子特效游戏物体 二、修改粒子系统属性 1. 基础属性 &#xff08;1&#xff09;修改发射粒子持续时间&#xff08;Duration&#xff09;为3s &#xff08;2&#xff09;取消勾选循环&#xff08;Looping&#xff09; &#xff08;2&#xff09;修改粒子存在时间&…

《梦醒蝶飞:释放Excel函数与公式的力量》9.4 NPV函数

9.4 NPV函数 NPV函数是Excel中用于计算净现值的函数。净现值&#xff08;Net Present Value, NPV&#xff09;是财务管理和投资决策中常用的指标&#xff0c;用于评估投资项目的价值。NPV表示的是未来一系列现金流的现值总和减去初始投资后的余额。 9.4.1 函数简介 NPV函数通…

【位运算】基础算法总结

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

【AI资讯】可以媲美GPT-SoVITS的低显存开源文本转语音模型Fish Speech

Fish Speech是一款由fishaudio开发的全新文本转语音工具&#xff0c;支持中英日三种语言&#xff0c;语音处理接近人类水平&#xff0c;使用Flash-Attn算法处理大规模数据&#xff0c;提供高效、准确、稳定的TTS体验。 Fish Audio

生产调度:flowshop问题数学建模

接上一篇文章&#xff0c;在了解生产调度问题的背景和基本概念之后&#xff0c;我想先从比较基础的 flowshop和 jobshop 数学模型入手&#xff0c;理解实际调度过程中的问题求解思路。这一篇文章主要面向 flowshop 问题进行数学建模&#xff0c;对于这类比较经典的问题&#xf…