【Spring】BeanName 的自动生成原理

news2025/1/11 10:59:19

🎈博客主页:🌈我的主页🌈
🎈欢迎点赞 👍 收藏 🌟留言 📝 欢迎讨论!👏
🎈本文由 【泠青沼~】 原创,首发于 CSDN🚩🚩🚩
🎈由于博主是在学小白一枚,难免会有错误,有任何问题欢迎评论区留言指出,感激不尽!🌠个人主页


目录

  • 🌟 一、默认 name 生成原理
  • 🌟 二、id 和 name 属性处理原理


🌟 一、默认 name 生成原理

在 Spring 中,提供了 BeanNameGenerator 用来生成 BeanName:

public interface BeanNameGenerator {

	/**
	 * Generate a bean name for the given bean definition.
	 * @param definition the bean definition to generate a name for
	 * @param registry the bean definition registry that the given definition
	 * is supposed to be registered with
	 * @return the generated bean name
	 */
	String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry);

}

在这里插入图片描述

  • DefaultBeanNameGenerator:XML 配置中,默认的 BeanName 就是在这个中自动生成的

  • AnnotationBeanNameGenerator:Java 配置中,如果使用了 @Component 等注解标记的 Bean,没有设置默认的名称,则通过这个来生成默认的 BeanName

public class DefaultBeanNameGenerator implements BeanNameGenerator {

	/**
	 * A convenient constant for a default {@code DefaultBeanNameGenerator} instance,
	 * as used for {@link AbstractBeanDefinitionReader} setup.
	 * @since 5.2
	 */
	public static final DefaultBeanNameGenerator INSTANCE = new DefaultBeanNameGenerator();


	@Override
	public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
		return BeanDefinitionReaderUtils.generateBeanName(definition, registry);
	}

}

可以看到,generateBeanName 这个方法实际上代理了 BeanDefinitionReaderUtils.generateBeanName 方法的执行,真正的 BeanName 的生成是在这个方法中完成的
在这里插入图片描述

public static String generateBeanName(
		BeanDefinition definition, BeanDefinitionRegistry registry, boolean isInnerBean)
		throws BeanDefinitionStoreException {
    // 这里就是获取到 XML 中 bean 标签里边配置的 class 属性的值
	String generatedBeanName = definition.getBeanClassName();
    // 判断是否有 class 这个属性值,如果没有的话,则在 parnetName 存在的情况下,
    // 使用 parentName+$child 来作为 生成的 beanName
	if (generatedBeanName == null) {
		if (definition.getParentName() != null) {
			generatedBeanName = definition.getParentName() + "$child";
		}
        // 如果没有 parentName,则尝试使用 factoryBeanName
		else if (definition.getFactoryBeanName() != null) {
			generatedBeanName = definition.getFactoryBeanName() + "$created";
		}
	}
    // 如果经过上面的处理,还是没有 generatedBeanName,那么就要抛异常了
	if (!StringUtils.hasText(generatedBeanName)) {
		throw new BeanDefinitionStoreException("Unnamed bean definition specifies neither " +
				"'class' nor 'parent' nor 'factory-bean' - can't generate bean name");
	}
	if (isInnerBean) {
		// Inner bean: generate identity hashcode suffix.
		return generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + ObjectUtils.getIdentityHexString(definition);
	}
	// Top-level bean: use plain class name with unique suffix if necessary.
    // 我们的默认 BeanName,实际上是在这个方法中生成的
	return uniqueBeanName(generatedBeanName, registry);
}
public static String uniqueBeanName(String beanName, BeanDefinitionRegistry registry) {
	String id = beanName;
	int counter = -1;
	// Increase counter until the id is unique.
    //GENERATED_BEAN_NAME_SEPARATOR 实际上就是 #
    // 所有这里是把类的全路径和 # 拼在一起
	String prefix = beanName + GENERATED_BEAN_NAME_SEPARATOR;
    // 后面的判断表示这个 id 是否已经被注册了,如果已经被注册,则继续生成新的 id
	while (counter == -1 || registry.containsBeanDefinition(id)) {
		counter++;
		id = prefix + counter;
	}
    //最终生成的 id 就是 org.javaboy.bean.User#0
	return id;
}

由此可以看到,默认的 BeanName 就是类的全路径+#+序列号,如 com.dong.Cat#0com.dong.Cat#1
对于序列号为 0 的 BeanName,还有一个默认的名称,就是类的全路径,不加任何序列号
上面这个生成 BeanName 的方法是在 BeanDefinitionParserDelegate#parseBeanDefinitionElement 方法中执行的,具体的逻辑如下:

if (beanDefinition != null) {
    // 当前没有配置 BeanName,即 bean 标签中没有 id 或者 name 属性
	if (!StringUtils.hasText(beanName)) {
		try {
			if (containingBean != null) {
				beanName = BeanDefinitionReaderUtils.generateBeanName(
						beanDefinition, this.readerContext.getRegistry(), true);
			}
			else {
                //这个地方,最终会调用到上面的逻辑去生成 BeanName
                //com.dong.Cat#0
				beanName = this.readerContext.generateBeanName(beanDefinition);
				// Register an alias for the plain bean class name, if still possible,
				// if the generator returned the class name plus a suffix.
				// This is expected for Spring 1.2/2.0 backwards compatibility.
                // 获取一个类的全路径 com.dong.Cat
                //!this.readerContext.getRegistry().isBeanNameInUse(beanClassName) 表示 beanClassName 还没有作为一个 BeanName 注册到 Spring 容器中
				String beanClassName = beanDefinition.getBeanClassName();
				if (beanClassName != null &&
						beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
						!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
					//将之添加别名中,相当于类的全路径本身,成为了 Bean 的一个别名
                    aliases.add(beanClassName);
				}
			}
			if (logger.isTraceEnabled()) {
				logger.trace("Neither XML 'id' nor 'name' specified - " +
						"using generated bean name [" + beanName + "]");
			}
		}
		catch (Exception ex) {
			error(ex.getMessage(), ele);
			return null;
		}
	}
	String[] aliasesArray = StringUtils.toStringArray(aliases);
	return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}

这就是为什么默认生成的 BeanName 中,#0可有可无的原因

🌟 二、id 和 name 属性处理原理

id 和 name 属性的处理其实也是在 BeanDefinitionParserDelegate#parseBeanDefinitionElement 方法中:


// 获取 bean 标签中的 id 属性值,user
String id = ele.getAttribute(ID_ATTRIBUTE);
// 获取 bean 标签中 name 属性值,user;user2;user3
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
List<String> aliases = new ArrayList<>();
if (StringUtils.hasLength(nameAttr)) {
    //MULTI_VALUE_ATTRIBUTE_DELIMITERS 变量实际上就是 ;,空格
    // 所以这个方法实际上就是根据 ; , 以及 空格 去拆分 nameAttr,将之拆分为一个数组
	String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
	// name 拆出来的属性将作为这个 bean 的别名
    aliases.addAll(Arrays.asList(nameArr));
}
//使用 id 作为 beanName
String beanName = id;
//这里相当于判断这个 bean 标签没有 id 属性,但是有 name 属性
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
    //将 name 拆出来的集合中的第一项作为 beanName
	beanName = aliases.remove(0);
	if (logger.isTraceEnabled()) {
		logger.trace("No XML 'id' specified - using '" + beanName +
				"' as bean name and " + aliases + " as aliases");
	}
}

但是,经过上面的处理,beanName 还是有可能为空。如果还为空,则进入到上面的逻辑中,自动生成 BeanName

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

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

相关文章

华为云云耀云服务器L实例评测|搭建Domain Admin环境监控公司网站的SSL证书,实现到期提醒

本文基于华为云云耀云服务器L实例下的场景体验开源软件Domain Admin的安装部署&#xff0c;实现SSL证书的到期提醒 目录 1、购买华为云耀云服务器L实例2、登录服务器2.1、重置密码2.2、连接服务器 3、安装Domain Admin3.1、检查Python3.2、检查pip3.3、安装Domain Admin3.4、启…

VUE之proxy配置实现跨域

什么是跨域 要了解跨域&#xff0c;首先得知道浏览器的同源策略。 同源策略&#xff1a;是由Netscape提出的一个安全策略&#xff0c;能够阻挡恶意文档&#xff0c;保护本地数据。它能限制一个源的文档或脚本对另一个源的交互&#xff0c;使得其它源的文档或脚本&#xff0c;…

皮克定理和多边形面积及应用

欢迎关注更多精彩 关注我&#xff0c;学习常用算法与数据结构&#xff0c;一题多解&#xff0c;降维打击。 皮克定理 皮克定理&#xff1a;皮克定理是指一个计算所有顶点坐标为整数的多边形面积公。该公式可以表示为Sab2&#xff0d;1&#xff0c;其中a表示多边形内部的坐标为…

Python list列表添加元素的3种方法

实际开发中&#xff0c;经常需要对​ Python 列表 ​进行更新&#xff0c;包括向列表中添加元素、修改表中元素以及删除元素。本节先来学习如何向列表中添加元素。 使用运算符可以将多个序列连接起来&#xff1b;列表是序列的一种&#xff0c;所以也可以使用进行连接&#xff0…

Linux配置成代理服务器

Linux配置成代理服务器 什么是代理服务器 把Linux配置成代理服务器 开放的代理服务器 升级需要账号密码的代理服务器 Linux系统使用代理服务器 临时通过代理访问 永久通过代理访问 Windows系统使用代理服务器 什么是代理服务器 代理服务器&#xff08;Proxy Server&am…

《动手学深度学习 Pytorch版》 4.5 权重衰减

4.5.1 范数与权重衰减 整节理论&#xff0c;详见书本。 4.5.2 高维线性回归 %matplotlib inline import torch from torch import nn from d2l import torch as d2l# 生成一些数据&#xff0c;为了使过拟合效果更明显&#xff0c;将维数增加到 200 并使用一个只包含 20 个样…

vue3中使用el-upload + tui-image-editor进行图片处理

效果如下 看之前请先看上一篇《vue3中使用组件tui-image-editor进行图片处理》中的 1、第一步安装 2、第二部封装组件 本篇只是在这基础上结合el-upload使用组件 3、第三步结合el-upload使用组件 <template><el-dialog:title"dialogTitle":modelValue&qu…

004-Windows下开发环境搭建

Windows下开发环境搭建 文章目录 Windows下开发环境搭建项目介绍版本控制工具Git 与 SVNWindow下安装Git Qt 开发工具静态编译Qt环境安装 串口模拟器比较工具SQLite 数据库查看小工具预告 关键字&#xff1a; Qt、 Qml、 开发环境、 Windows、 C 项目介绍 欢迎来到我们的 …

Elastic Universal Profiling™ 是一种连续分析解决方案,现已正式上市

作者&#xff1a;Israel Ogbole, Stephanie Boomsma 在软件无处不在且影响深远的世界中&#xff0c;效率的重要性不仅仅局限于业务范围。 高效的软件不仅有利于企业&#xff0c;而且有利于企业。 这对全球也有好处。 通过将计算效率作为核心目标&#xff0c;并为专业人员提供正…

WebGL光照介绍——平行光、环境光下的漫反射

目录 光照原理 光源类型 平行光 点光源 环境光 反射类型 漫反射 漫反射光颜色 计算公式 环境反射 环境反射光颜色 表面的反射光颜色&#xff08;漫反射和环境反射同时存在时&#xff09;计算公式 平行光下的漫反射 根据光线和法线方向计算入射角θ&#xff08;以便…

数据结构--哈希表,哈希函数(或者散列表、散列函数)

目录 哈希表的定义 处理冲突的方法--拉链法 散列查找 常见的散列函数&#xff08;构造哈希函数&#xff09; 除留余数法 直接定址法 数字分析法 平方取中法 处理冲突的方法--开放定址法 &#xff08;1&#xff09;线性探测法&#xff1a; &#xff08;2&#xff09…

笔记:Android 应用启动流程

1.点击图标&#xff0c;启动app 点击图标&#xff0c;实际是封装了一个 Intent 然后调用了 startActivity 方法 ComponentName componentName new ComponentName("包名", "activity 名称"); Intent intent new Intent(Intent.ACTION_MAIN); intent.set…

【问题处理】GIT合并解决冲突后,导致其他人代码遗失的排查

GIT合并解决冲突后&#xff0c;导致其他人代码遗失的排查 项目场景问题描述分析与处理&#xff1a;1. 警告分析2. 文件分析3. 问题关键4. 验证 解决策略总结 &#x1f4d5;作者简介&#xff1a;战斧&#xff0c;从事金融IT行业&#xff0c;有着多年一线开发、架构经验&#xff…

【Nacos】Nacos服务注册服务端源码分析(一)

上篇简单看了下Nacos客户端在服务注册时做了什么。 本篇开始分析Nacos在服务注册时&#xff0c;服务端的相关逻辑。 建议先阅读这篇文章&#xff1a;支持 gRPC 长链接&#xff0c;深度解读 Nacos 2.0 架构设计及新模型 回顾一下&#xff0c;上篇我们看了Nacos在服务注册时&a…

四种常用的自动化测试框架

一直想仔细研究框架&#xff0c;写个流水账似的测试程序不难&#xff0c;写个低维护成本的测试框架就很难了&#xff0c;所以研究多种测试框架还是很有必要的&#xff0c;知道孰优孰劣&#xff0c;才能在开始编写框架的时候打好基础&#xff0c;今天读到了KiKi Zhao的翻译文章&…

reverse_iterator 逆序迭代器

在上一篇 C迭代器 iterator&#xff08;与逆序迭代器&#xff09; 中&#xff0c;我们看到了迭代器的基本用法。 现在我们看看一个奇怪的现象 意料之外的减法运算 无符号数的 0 减 1 变成了什么&#xff1f; &#xff1f;已知 size_t 一般为 unsigned long long 类型。 #…

区块链(4):区块链去中心化

1 区块链白皮书中的公有链&#xff0c;私有链&#xff0c;联盟链概念介绍 区块链系统根据应用场景和设计体系的不同&#xff0c;一般分为公有链、联盟 链和专有链(私有链)。其中: 公有链的各个节点可以自由加入和退出网络&#xff0c;并参加链上数据的读 写&#xff0c;运行时…

SpringBoot+MyBatis flex实现简单增删改查

一&#xff1a;创建SpringBoot项目 SpringBoot版本选择2.7.15 勾选相关的选项&#xff0c;并点击Create 项目创建完成 二.pom文件添加相关的依赖 <dependencies><dependency><groupId>io.springfox</groupId><artifactId>springfox-boot-starte…

排序——直接插入排序折半插入排序

文章目录 前言一、排序的基本概念1. 排序的定义2. 排序的分类1) 稳定排序2) 不稳定排序 二、插入排序1. 直接插入排序1&#xff09;直接插入排序算法分析 2. 直接插入排序代码3. 直接插入排序时间复杂度4. 折半插入排序5. 折半插入排序代码 总结 前言 排序的基本概念数据结构稳…

从数字化到智能化再到智慧化,智慧公厕让城市基础配套更“聪明”

随着科技的迅猛发展&#xff0c;城市生活方式与配置设施的方方面&#xff0c;面也在不断的改变和升级。智慧公厕作为城市基础配套设施的一部分&#xff0c;从数字化到智能化再到智慧化&#xff0c;正逐渐展现出其独特的魅力和优势。实现了公共厕所建设、使用与管理方式的全面变…