深入分析 Spring 中 Bean 名称的加载机制

目录

前言

通过前文:《深入分析-Spring BeanDefinition构造元信息》一文我们可以了解到:Spring Framework共有三种方式可以定义Bean,分别为:XML配置文件、注解、Java配置类, 从Spring Framework 3.0(2019年12月发布)版本开始推荐使用注解来定义Bean,而不是XML配置文件,因此,本文的重点是放在探索Spring Framework如何从使用注解定义的Bean元数据中获取到Bean的名称。

AnnotationBeanNameGenerator类的介绍

作用

AnnotationBeanNameGenerator在Spring Framework中用于生成基于注解的Bean名称,其主要作用是根据指定的注解信息,生成符合规范的Bean名称。它在Spring容器初始化时,通过扫描注解配置的组件类,并且根据其定义的命名规则生成Bean名称,然后将这些名称与对应的Bean实例关联起来。

如:你在工程中使用@Service注解定义了一个HelloService的Bean,那么你在启动SpringBoot工程后,该Bean会以beanName为“helloService”注入到Spring容器中。

/*** @author 公众号:种棵代码技术树*/
@Service
public class HelloService {private final Logger logger = LoggerFactory.getLogger(HelloService.class);private final HelloAsyncService helloAsyncService;/*** Instantiates a new Hello service.** @param helloAsyncService the hello async service*/public HelloService(HelloAsyncService helloAsyncService) {this.helloAsyncService = helloAsyncService;}
}

计算代码中用于返回Bean名称的StringUtils.uncapitalizeAsProperty(shortClassName);即可得到:

同时还可以看到上一篇文章:《深入分析-Spring BeanDefinition构造元信息》中有关BeanDefinition的内容,如:Bean的全限定类名和作用域。

继承关系

AnnotationBeanNameGeneratorBeanNameGenerator接口的实现类,该接口的主要功能是为给定的Bean生成唯一的名称。目前,BeanNameGenerator接口有两个实现,除了本篇文章介绍的AnnotationBeanNameGenerator外,还有默认实现类DefaultBeanNameGeneratorDefaultBeanNameGenerator主要用于处理通过XML文件定义的Bean,为其自动生成名称。FullyQualifiedAnnotationBeanNameGenerator继承自AnnotationBeanNameGenerator,同样属于BeanNameGenerator接口的实现类,该类覆写了AnnotationBeanNameGeneratorbuildDefaultBeanName()方法,作用是使用注解类型和注解元数据,结合其他信息(例如类名、包名等),生成带有完全限定名的Bean名称。

源码结构

  1. 类声明部分:定义了AnnotationBeanNameGenerator类,并实现了BeanNameGenerator接口。
  2. 日志处理部分:定义了一个静态的Log对象logger,用于记录日志信息。
  3. Bean名称生成方法:实现了generateBeanName()方法,用于根据给定的Bean定义生成Bean名称。如果Bean定义是一个带注解的Bean定义,会调用determineBeanNameFromAnnotation()方法来基于注解生成Bean名称;否则会使用默认的Bean名称生成策略buildDefaultBeanName()方法来生成Bean名称。
  4. 注解处理部分:定义了determineBeanNameFromAnnotation()方法和isStereotypeWithNameValue()方法,用于判断是否需要处理注解元数据,从中获取Bean名称。
  5. 默认Bean名称生成策略部分:实现了buildDefaultBeanName()方法和getComponentAnnotation()方法,用于生成默认的Bean名称。
  6. 其他辅助方法:例如isStereotypeWithNameValue()方法和getComponentAnnotation()方法,用于支持上述方法的实现。

当@value配置值时:

@Service(value = "HelloService")

实现原理

@Override
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {if (definition instanceof AnnotatedBeanDefinition) {String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);if (StringUtils.hasText(beanName)) {// Explicit bean name found.return beanName;}}// Fallback: generate a unique default bean name.return buildDefaultBeanName(definition, registry);
}

如果当前BeanDefinitionAnnotationBeanNameGenerator类型,则尝试从注解中获取Bean的名称,如果找了BeanName,则直接返回。

	/*** Derive a bean name from one of the annotations on the class.* @param annotatedDef the annotation-aware bean definition* @return the bean name, or {@code null} if none is found*/@Nullableprotected String determineBeanNameFromAnnotation(AnnotatedBeanDefinition annotatedDef) {AnnotationMetadata amd = annotatedDef.getMetadata();Set<String> types = amd.getAnnotationTypes();String beanName = null;for (String type : types) {AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(amd, type);if (attributes != null) {Set<String> metaTypes = this.metaAnnotationTypesCache.computeIfAbsent(type, key -> {Set<String> result = amd.getMetaAnnotationTypes(key);return (result.isEmpty() ? Collections.emptySet() : result);});if (isStereotypeWithNameValue(type, metaTypes, attributes)) {Object value = attributes.get("value");if (value instanceof String) {String strVal = (String) value;if (StringUtils.hasLength(strVal)) {if (beanName != null && !strVal.equals(beanName)) {throw new IllegalStateException("Stereotype annotations suggest inconsistent " +"component names: '" + beanName + "' versus '" + strVal + "'");}beanName = strVal;}}}}}return beanName;}

从某个注解中获取Bean名称,该方法是主要的BeanName获取逻辑,其大体逻辑为:

  1. 从Bean的元注解获取数据,遍历源数据中的数据。
  2. 获取元数据的类型,如果元数据已被注入到容器池中,则直接返回结果。
  3. 如果注解是否允许通过@Value注解来获取bean名称,如果可以通过@Value注解获取Bean名称,则使用元数据中@Value定义的信息为Bean名称,最后返回,放入如果元数据中未配置@Value相关数据,则返回null。
  4. 当然,@Value中是可以不配置信息的,此时执行fallBack,即调用 buildDefaultBeanName 方法生成一个默认的 Bean 名称,并返回。
	/*** Derive a default bean name from the given bean definition.* <p>The default implementation delegates to {@link #buildDefaultBeanName(BeanDefinition)}.* @param definition the bean definition to build a bean name for* @param registry the registry that the given bean definition is being registered with* @return the default bean name (never {@code null})*/protected String buildDefaultBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {return buildDefaultBeanName(definition);}/*** Derive a default bean name from the given bean definition.* <p>The default implementation simply builds a decapitalized version* of the short class name: e.g. "mypackage.MyJdbcDao" -> "myJdbcDao".* <p>Note that inner classes will thus have names of the form* "outerClassName.InnerClassName", which because of the period in the* name may be an issue if you are autowiring by name.* @param definition the bean definition to build a bean name for* @return the default bean name (never {@code null})*/protected String buildDefaultBeanName(BeanDefinition definition) {String beanClassName = definition.getBeanClassName();Assert.state(beanClassName != null, "No bean class name set");String shortClassName = ClassUtils.getShortName(beanClassName);return Introspector.decapitalize(shortClassName);}

该方法的作用是:从给定的 Bean 定义派生缺省 Bean 名称。

默认实现只是构建短类名的去大写版本:例如“mypackage.MyJdbcDao“ -> ”myJdbcDao”。

经过以上代码,每个Bean均会获得其对应的BeanName。

总结

AnnotationBeanNameGenerator 的优点有:

  1. 自动生成唯一的 Bean 名称,避免了手动命名时出现重名的情况;
  2. 提高了代码可读性和可维护性,因为通过注解来指定 Bean 名称可以更直观地表达 Bean 的含义;
  3. 灵活性较高,支持多种类型的注解,例如 @Service、@Component、@Repository 等。

AnnotationBeanNameGenerator 的缺点则是:

  1. 如果注解中未指定 Bean 名称,该生成器会默认使用类名作为 Bean 名称,这可能导致出现多个类名相同的 Bean,需要特别注意;
  2. 由于生成的 Bean 名称是自动生成的,因此有时可能不太符合开发者的命名习惯,需要手动修改 Bean 的名称。

AnnotationBeanNameGenerator 在实际开发中可以帮助开发者快速生成唯一的 Bean 名称,提高代码的可读性和可维护性,但需要特别注意类名重复以及自动生成的名称是否符合需求。

后续内容文章持续更新中…

近期发布。


关于我

👋🏻你好,我是Debug.c。微信公众号:种棵代码技术树 的维护者,一个跨专业自学Java,对技术保持热爱的bug猿,同样也是在某二线城市打拼四年余的Java Coder。

🏆在掘金、CSDN、公众号我将分享我最近学习的内容、踩过的坑以及自己对技术的理解。

📞如果您对我感兴趣,请联系我。

若有收获,就点个赞吧,喜欢原图请私信我。

wallhaven-exrqrr.jpg

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

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

相关文章

快速了解——逻辑回归及模型评估方法

一、逻辑回归 应用场景&#xff1a;解决二分类问题 1、sigmoid函数 1. 公式&#xff1a; 2. 作用&#xff1a;把 (-∞&#xff0c;∞) 映射到 (0&#xff0c; 1) 3. 数学性质&#xff1a;单调递增函数&#xff0c;拐点在x0&#xff0c;y0.5的位置 4. 导函数公式&#xff1a;f…

让java程序就像脚本一样去写工具

背景&#xff1a; 接触了各种语言之后发现&#xff0c;java还是比go&#xff0c;.netcore之类的简单&#xff0c;成熟&#xff0c;我最终选择了jenkinsshelljava去部署我们的代码&#xff0c;此时很多人可能去使用js或者python之类的去写部署逻辑&#xff0c;毕竟java每次打包…

时序预测 | Matlab实现EEMD-SSA-BiLSTM、EEMD-BiLSTM、SSA-BiLSTM、BiLSTM时序预测对比

时序预测 | Matlab实现EEMD-SSA-BiLSTM、EEMD-BiLSTM、SSA-BiLSTM、BiLSTM时间序列预测对比 目录 时序预测 | Matlab实现EEMD-SSA-BiLSTM、EEMD-BiLSTM、SSA-BiLSTM、BiLSTM时间序列预测对比预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.Matlab实现EEMD-SSA-BiLSTM、…

计算机缺失msvcr100.dll如何修复?分享五种实测靠谱的方法

在计算机系统的日常运行与维护过程中&#xff0c;我们可能会遇到一种特定的故障场景&#xff0c;即系统中关键性动态链接库文件msvcr100.dll的丢失。msvcr100.dll是Microsoft Visual C Redistributable Package的一部分&#xff0c;对于许多基于Windows的应用程序来说&#xff…

Open3D 获取点云坐标最值(17)

Open3D 获取点云坐标最值(17) 一、算法介绍二、算法实现1.代码2.结果人生天地间,忽如远行客 一、算法介绍 快速获取点云块,沿着 x y z 各方向的坐标最值,这些在点云处理中的应用范围是如此广泛,这也是点云最常被用到的关键信息,后续的很多算法都会设置到这一处理方法。…

云端绘影,让青玉案跃然眼前

编辑&#xff1a;阿冒 设计&#xff1a;沐由 “东风夜放花千树&#xff0c;更吹落&#xff0c;星如雨。宝马雕车香满路。凤箫声动&#xff0c;玉壶光转&#xff0c;一夜鱼龙舞。” 每每诵读这首《青玉案》&#xff0c;那种花灯耀眼、乐声盈耳的元夕盛况就会立刻浮现在脑海中&am…

【开源】基于JAVA+Vue+SpringBoot的校园电商物流云平台

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 商品数据模块2.3 快递公司模块2.4 物流订单模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 商品表3.2.2 快递公司表3.2.3 物流订单表 四、系统展示五、核心代码5.1 查询商品5.2 查询快递公司5.3 查…

java..类文件具有错误的版本 61.0, 应为 55.0 请删除该文件或确保该文件位于正确的类路径子目录中。

第一步先打开pom.xml配置文件&#xff0c;找到版本号那几行&#xff0c;这里需要更改版本号。 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/X…

一文读懂ERP、OMS、WMS、TMS(1/2)

目录 一、ERP、OMS、WMS、TMS的定义 1.1 ERP&#xff08;Enterprise Resource Planning&#xff09; 1.2 OMS&#xff08;Order Management System&#xff09; 1.3 WMS&#xff08;Warehouse Management System&#xff09; 1.4 TMS &#xff08;Transportation Manageme…

【SpringCloud】之网关应用(进阶使用)

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是君易--鑨&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的博客专栏《SpringCloud开发之网关应用》。&#x1f3af;&a…

微机原理常考简答题(二)

一&#xff0c;简述8086CPU响应可屏蔽中断的条件及过程。 CPU响应可屏蔽中断的条件是有中断请求&#xff0c;中断标志IF1开中断&#xff0c;现行指令执行结束。 CPU响应可屏蔽中断的过程&#xff1a;CPU在INTR引脚上接到一个中断请求信号&#xff0c;如果此时IF1&#xff0c;并…

group by 查询慢的话,如何优化?

1、说明 根据一定的规则&#xff0c;进行分组。 group by可能会慢在哪里&#xff1f;因为它既用到临时表&#xff0c;又默认用到排序。有时候还可能用到磁盘临时表。 如果执行过程中&#xff0c;会发现内存临时表大小到达了上限&#xff08;控制这个上限的参数就是tmp_table…

Vue3函数式弹窗实现

要在一些敏感操作进行前要求输入账号和密码&#xff0c;然后将输入的账号和密码加到接口请求的header里面。如果每个页面都去手动导入弹窗组件&#xff0c;在点击按钮后弹出弹窗。再拿到弹窗返回的账号密码后去请求接口也太累了&#xff0c;那么有没有更简单的实现方式呢&#…

强化学习(一)简介

强化学习这一概念在历史上来源于行为心理学&#xff0c;来描述生物为了趋利避害而改变自己行为的学习过程。人类学习的过程其实就是为达到某种目的不断地与环境进行互动试错&#xff0c;比如婴儿学习走路。强化学习算法探索了一种从交互中学习的计算方法。 1、强化学习 强化学…

kylin3集群问题和思考(单机转集群)

目录 单机改集群注意事项 问题 思考 建议 单机改集群注意事项 之前是使用的单机版&#xff0c;但后面查询压力过大&#xff0c;一个方案是改成集群。 由于是同一个集群的&#xff0c;元数据没有变化&#xff0c;所以&#xff0c;直接将原本的kylin使用scp的方式发送到其他节…

【重学C语言】二、前期准备和第一个C程序

【重学C语言】二、前期准备和第一个C程序 1. VS 项目1.1 创建项目 2. Clion 项目(本博主主用)2.1 创建项目2.2 Clion 配置 3. 构建类型4. 构建模式5. 注释6. 第一个 C 程序7. 程序闪退8. 新手遇到的问题 1. VS 项目 1.1 创建项目 打开 VS 创建新项目 创建 main.c 书写以下…

【LabVIEW FPGA入门】模拟输入和模拟输出

1.简单模拟输入和输出测试 1.打开项目&#xff0c;在FPGA终端下面新建一个VI 2.本示例以模拟输入卡和模拟输出卡同时举例。 3.新建一个VI编写程序&#xff0c;同时将卡1的输出连接到卡2的输入使用物理连线。 4.编译并运行程序&#xff0c;观察是否能从通道中采集和输出信号。 5…

【LeetCode】19. 删除链表的倒数第 N 个结点(中等)——代码随想录算法训练营Day04

题目链接&#xff1a;19. 删除链表的倒数第 N 个结点 题目描述 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], n 2 输出&#xff1a;[1,2,3,5] 示例 2&#xff1a; 输入&a…

行为型设计模式——迭代器模式

迭代器模式 迭代器模式也是非常的简单&#xff0c;定义如下&#xff1a; 提供一个对象来顺序访问聚合对象中的一系列数据&#xff0c;而不暴露聚合对象的内部表示。 相信大家都使用过类似下面的迭代器&#xff1a; List<String> list new ArrayList<>(); Iterat…

通过shell脚本确定当前平台

shell中的变量OSTYPE存储操作系统的名称&#xff0c;也可以使用uname命令来确认当前所在的平台。 shell中的变量HOSTTYPE存储操作系统的架构。 测试代码如下所示&#xff1a; #! /bin/bashecho "use OSTYPE:" if [[ "$OSTYPE" "linux-gnu&quo…