当前位置: 首页 > news >正文

解析 Spring 框架中的三种 BeanName 生成策略

在 Spring 框架中,定义 Bean 时不一定需要指定名称,Spring 会智能生成默认名称。本文将介绍 Spring 的三种 BeanName 生成器,包括在 XML 配置、Java 注解和组件扫描中使用的情况,并解释它们如何自动创建和管理 Bean 名称。

1. BeanNameGenerator

Spring 中提供了一个名为 BeanNameGenerator 的接口,这个接口就只有一个需要实现的方法就是 generateBeanName,从名字就能看出来,这就是专门用来生成 beanName 的方法。

public interface BeanNameGenerator {String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry);
}

这个方法有两个参数:

  • definition:这个是要生成的 Bean 定义。
  • registry:这个是将来 BeanDefinition 的注册器。

BeanNameGenerator 有三个不同的实现类,对应不同的处理场景:

  • AnnotationBeanNameGenerator:这个专门用来处理包扫描的时候扫到的 Bean,对于这些 Bean,其 name 属性该如何处理,由这个类来解决,当然,小伙伴们都知道,通过 @Component/@Service/@Repository/
    @Controller 这些注解定义的 Bean,默认情况下,beanName 就是类名首字母小写。
  • FullyQualifiedAnnotationBeanNameGenerator:这个继承自 AnnotationBeanNameGenerator,并重写了 AnnotationBeanNameGenerator#buildDefaultBeanName 方法,这个是使用类的全路径来作为 Bean 的默认名称。
  • DefaultBeanNameGenerator:这个是专门用来解决 XML 文件中定义的 Bean 如果没有设置 beanName,那么就通过 DefaultBeanNameGenerator 来为其生成 beanName。

看了上面三个场景之后,可能有小伙伴发现一个 BUG,那么 
 注解定义的 Bean,其 beanName 属性是在哪里处理的呢?这个其实比较特殊,是当场处理的,没用到 BeanNameGenerator,松哥后面单独说。

接下来我们详细看下上面这三个实现类。

2. AnnotationBeanNameGenerator

咱们直接来看最关键的 generateBeanName 方法吧:

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

这个方法首先判断 definition 是否为 AnnotatedBeanDefinition 类型,根据我们前面文章对 BeanDefinition 的介绍,大家知道,AnnotatedBeanDefinition 的实现类主要是针对三种情况:@Bean 注解定义的 Bean、@Service/@Controller/@Component/@Repository 等注解标记的 Bean 以及系统的启动配置类,如果是这三种情况,那么就去调用 determineBeanNameFromAnnotation 方法,这个方法会尝试从注解中提取出来 beanName,如果不是上面三种情况,那么就调用 buildDefaultBeanName 方法去生成 beanName。

那我们先来看 determineBeanNameFromAnnotation 方法:

@Nullable
protected 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 -&gt; {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 strVal) {if (StringUtils.hasLength(strVal)) {if (beanName != null &amp;&amp; !strVal.equals(beanName)) {throw new IllegalStateException("Stereotype annotations suggest inconsistent " +"component names: '" + beanName + "' versus '" + strVal + "'");}beanName = strVal;}}}}}return beanName;
}

这个方法首先会去获取类上的注解信息,拿到 amd 之后,获取到所有的注解类型,然后进行遍历。

遍历的时候,首先获取到注解上的所有属性 attributes,当 attributes 不为空的时候,继续去读取当前注解的元注解,并将读取到的结果存入到 metaAnnotationTypesCache 集合中。这个是干嘛呢?大家知道,Spring 中用来标记 Bean 的注解大部分衍生自 @Component,甚至我们也可以自定义注解,那么如果自定义注解了,这个地方就没法判断了,因为每个人自定义出来的注解都不一样。所以,万变不离其宗,这里就去找各个注解的元注解。例如如果我们在类上添加的是 @Configuration,那么 @Configuration 的元注解有两个,分别是 @Component 和 @Indexed。

接下来的 isStereotypeWithNameValue 方法就是判断 type 是不是 @Component 或者 Jakarta 中自带的 @ManagedBean、@Named,亦或者 metaTypes 里是否包含 @Component。如果确定是 @Component 衍生出来的注解,亦或者是 @ManagedBean、@Named 注解标记的 Bean,那么就将其 value 属性读取出来,作为 beanName,如果包含多个有效注解,且各自配置的 beanName 不一致,就会抛出异常。

例如下面这种情况:

@Configuration("j")
@Component("a")
public class JavaConfig {
}

这两个 beanName 不一致,运行时就会出错。

同时,经过上面的分析,小伙伴也看到了,我们其实可以通过自定义注解为 Bean 设置名称,例如我有如下注解:

@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface MyBeanName {String value() default "";
}

这个注解衍生自 @Component,那么它的用法如下:

@MyBeanName("f")
public class JavaConfig {}

那么 f 就是当前类生成的 beanName。

以上是从注解中去提取 beanName,但是注解中可能没有提供 beanName,那么就得调用 buildDefaultBeanName 方法去自动生成了,如下:

protected String buildDefaultBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {return buildDefaultBeanName(definition);
}
protected String buildDefaultBeanName(BeanDefinition definition) {String beanClassName = definition.getBeanClassName();Assert.state(beanClassName != null, "No bean class name set");String shortClassName = ClassUtils.getShortName(beanClassName);return StringUtils.uncapitalizeAsProperty(shortClassName);
}

这个就很好懂了,先拿到 bean 的完整类名,然后提取出来 shortName,也就是去除包名之后的名字,然后首字母小写之后返回。

这就是 @Component 注解体系下的 beanName 生成流程。

3. FullyQualifiedAnnotationBeanNameGenerator

FullyQualifiedAnnotationBeanNameGenerator 类只是重写了 AnnotationBeanNameGenerator 的 buildDefaultBeanName 方法,如下:

@Override
protected String buildDefaultBeanName(BeanDefinition definition) {String beanClassName = definition.getBeanClassName();Assert.state(beanClassName != null, "No bean class name set");return beanClassName;
}

重写后的方法就是获取类的完整路径返回。

FullyQualifiedAnnotationBeanNameGenerator 默认情况下并不会直接使用,需要自己手动配置,像下面这样:

@Configuration
@ComponentScan(nameGenerator = FullyQualifiedAnnotationBeanNameGenerator.class)
public class JavaConfig {}

此时,生成的 Bean 的默认名称就是类的全路径了。

4. DefaultBeanNameGenerator

这个是专门用来处理 XML 中默认的 beanName 的。这个在最近录制的 Spring 源码视频中已经详细介绍过了,这里就不再啰嗦了。。

5. @Bean 处理特殊情况

如果类是被 @Bean 注解标记的,那么处理情况就特殊一些,直接现场处理,方法在 org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForBeanMethod 位置:

private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {// Consider name and any aliasesList<string> names = new ArrayList&lt;&gt;(Arrays.asList(bean.getStringArray("name")));String beanName = (!names.isEmpty() ? names.remove(0) : methodName);// Register aliases even when overriddenfor (String alias : names) {this.registry.registerAlias(beanName, alias);}
}

从这里可以看到,如果一开始配置了 name 属性,那么就把 names 集合中的第一个值拿出来作为 beanName,集合中的其他值则当作别名来处理,如果没有配置 name 属性值,那么就使用方法名作为 beanName。

相关文章:

解析 Spring 框架中的三种 BeanName 生成策略

在 Spring 框架中&#xff0c;定义 Bean 时不一定需要指定名称&#xff0c;Spring 会智能生成默认名称。本文将介绍 Spring 的三种 BeanName 生成器&#xff0c;包括在 XML 配置、Java 注解和组件扫描中使用的情况&#xff0c;并解释它们如何自动创建和管理 Bean 名称。 1. Be…...

细说ARM MCU的串口接收数据的实现过程

目录 一、硬件及工程 1、硬件 2、软件目的 3、创建.ioc工程 二、 代码修改 1、串口初始化函数MX_USART2_UART_Init() &#xff08;1&#xff09;MX_USART2_UART_Init()串口参数初始化函数 &#xff08;2&#xff09;HAL_UART_MspInit()串口功能模块初始化函数 2、串口…...

000-基于sklearn的机器学习入门:工作环境搭建与配置

本专栏将介绍基于Scikit-learn(简称sklearn)的机器学习入门知识。包括但不一定限于&#xff0c;机器学习基本知识、sklearn库简介&#xff0c;基于Sklearn库的机器学习实践。 这是本专栏的第000篇&#xff0c;将介绍如何安装和配置sklearn环境&#xff0c;不仅包括Sklearn库的…...

就业班 第四阶段(k8s) 2401--6.5 day3 Yaml语法解析+钩子函数

Yaml语法解析 今天学的都是在pod里面操作的 格式 这个文件要创建的资源对象是什么 kind: Pod 这个资源对象所在的api版本是什么 apiVersion: v1 元数据 metadata: 对这个pod中的镜像的描述 spec&#xff1a; 字典无序 同一级可以调换顺序 &#xff1a;比如kind和apiVersion …...

电脑开机出现英文字母,如何解决这个常见问题?

电脑开机时出现英文字母的情况通常意味着系统在启动过程中遇到了问题。这些英文字母可能是错误信息、系统提示或BIOS设置问题。通过理解这些信息并采取适当的措施&#xff0c;您可以解决大多数启动问题。本文将介绍三种解决电脑开机出现英文字母问题的方法&#xff0c;帮助您恢…...

一张试卷

目录 问题 1: 1.时间 题目描述1 输入1 输出1 样例输入1 样例输出1 提示1 代码1 问题 2: 超酷的电话号码 题目描述2 输入2 输出2 样例输入2 样例输出2 提示2 代码2 问题 3:3.爸爸的数学题 题目描述3 输入3 输出3 样例输入3 样例输出3 提示3 代码3 问题 4: 4. 营养膳食 题目描述4…...

记一次 .NET某游戏币自助机后端 内存暴涨分析

一&#xff1a;背景 1. 讲故事 前些天有位朋友找到我&#xff0c;说他们的程序内存会偶发性暴涨&#xff0c;自己分析了下是非托管内存问题&#xff0c;让我帮忙看下怎么回事&#xff1f;哈哈&#xff0c;看到这个dump我还是非常有兴趣的&#xff0c;居然还有这种游戏币自助机…...

计算机考研|哪些985/211院校不歧视双非二本生?

说句残酷的话&#xff0c;能对某一专业做出贡献&#xff0c;那么你的水平肯定是很高的。如果普通学生&#xff0c;普通本科的话可能很难做到这一点。这也是现在考研风气比较强的原因&#xff0c;一部分专业能力不突出的学生来选择深造3年。 对于基础较差想要考计算机研究生的同…...

Spring Boot:简化 Java 应用开发的艺术

Spring Boot 是一种用于快速开发、运行和管理 Java 应用程序的开源框架。它简化了基于 Spring 的应用程序的配置和部署&#xff0c;使得开发者能够更加专注于业务逻辑的实现。本文将介绍 Spring Boot 的核心特性、优势以及如何在项目中使用 Spring Boot。 一、核心特性 自动配…...

elasticsearch安装与使用(2)-基于term匹配的简单搜索引擎搭建

把一篇pdf论文解析后&#xff0c;放入es数据库中&#xff0c;建立倒排索引表&#xff0c;并实现简单搜索。 1、pdf论文解析(英文) 安装pdf解析包 pip install pdfminer.sixdef extract_text_from_pdf(filename, page_numbersNone, min_line_length1):从pdf文件中提取文字:pa…...

速盾:ddos防护与高防ip区别?

在网络安全领域&#xff0c;DDOS 防护和高防 IP 都是重要的防护手段&#xff0c;但它们之间存在着一些明显的区别。 DDOS 防护是一种针对分布式拒绝服务攻击的防御策略。它通过多种技术和方法来识别和抵御 DDOS 攻击。常见的 DDOS 防护手段包括流量清洗、连接限制、协议分析等。…...

Java中StringBulider详解

StringBuilder 是 Java 中一个用来创建可变字符串的类。与 String 类不同&#xff0c;StringBuilder 对象能够被修改&#xff0c;不会创建新的对象&#xff0c;因此在需要进行大量字符串操作时&#xff0c;StringBuilder 更高效。以下是 StringBuilder 的详细讲解。 创建 Stri…...

基于springboot高校就业招聘系统的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;就业咨询管理&#xff0c;毕业去向管理&#xff0c;简历管理&#xff0c;管理员管理&#xff0c;基础数据管理 辅导员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;就业咨询管理…...

嵌入式C语言编码规范要点

1.函数命名方法 骆驼命名法&#xff08;Camel&#xff09; 帕斯卡命名法&#xff08;&#xff08;pascal&#xff09;&#xff0c;也叫大驼峰命名法&#xff08;Upper Camel Case&#xff09; 匈牙利命名法 下划线命名法&#xff08;也称为蛇形命名法&#xff09; 详述见之前文…...

Python中的全局解释器锁:深入解析与应对策略

1. 引言 在Python的世界里&#xff0c;全局解释器锁&#xff08;GIL&#xff09;是一个经常被讨论的话题。它既是Python并发编程中的一个重要概念&#xff0c;也是许多开发者感到困惑的源头。本文将深入探讨GIL的工作原理、它对Python程序性能的影响 2. 全局解释器锁的历史背…...

【java计算机毕设】图书商城管理系统MySQL springboot vue html maven送文档

1项目功能介绍 【java计算机毕设】图书商城管理系统 Java Spring Boot vue HTML MySQL 赠送文档 PPT 2项目简介 系统功能&#xff1a; 图书商城管理系统包括管理员和用户两种角色。 管理员的功能包括在个人中心修改个人信息&#xff0c;以及在基础数据管理中管理会员等级类型和…...

【Java刷题】二叉树

相同的树 public boolean isSameTree(TreeNode p, TreeNode q) {if(p null && q null) {return true;} else if(p ! null && q ! null) {if(p.val ! q.val) {return false;} else {return isSameTree(p.left, q.left) && isSameTree(p.right, q.rig…...

【Linux】程序地址空间之动态库的加载

我们先进行一个整体轮廓的了解&#xff0c;随后在深入理解细节。 在动态库加载之前还要说一下程序的加载&#xff0c;因为理解了程序的加载对动态库会有更深的理解。 轮廓&#xff1a; 首先&#xff0c;不管是程序还是动态库刚开始都是在磁盘中的&#xff0c;想要执行对应的可…...

LabVIEW处理大量数据时,怎样确保数据的准确性和完整性?

在LabVIEW处理中&#xff0c;确保大量数据的准确性和完整性至关重要。以下是详细的多角度分析和建议&#xff0c;以确保在LabVIEW中处理大量数据时&#xff0c;数据的准确性和完整性&#xff1a; 1. 数据采集阶段 1.1 高精度硬件选择 选择高精度的数据采集硬件&#xff0c;如…...

容器是什么?

概念 容器可以被看作是一种轻量级的虚拟化技术。与传统虚拟化技术相比&#xff0c;容器不需要为每个应用程序提供单独的操作系统&#xff0c;它们共享宿主机的操作系统内核。这使得容器更加轻便和高效。 想象一下&#xff0c;容器就像是一艘艘可以在海洋中独立航行的货轮&…...

SciencePlots——绘制论文中的图片

文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了&#xff1a;一行…...

Module Federation 和 Native Federation 的比较

前言 Module Federation 是 Webpack 5 引入的微前端架构方案&#xff0c;允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...

Caliper 配置文件解析:config.yaml

Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...

ios苹果系统,js 滑动屏幕、锚定无效

现象&#xff1a;window.addEventListener监听touch无效&#xff0c;划不动屏幕&#xff0c;但是代码逻辑都有执行到。 scrollIntoView也无效。 原因&#xff1a;这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作&#xff0c;从而会影响…...

浪潮交换机配置track检测实现高速公路收费网络主备切换NQA

浪潮交换机track配置 项目背景高速网络拓扑网络情况分析通信线路收费网络路由 收费汇聚交换机相应配置收费汇聚track配置 项目背景 在实施省内一条高速公路时遇到的需求&#xff0c;本次涉及的主要是收费汇聚交换机的配置&#xff0c;浪潮网络设备在高速项目很少&#xff0c;通…...

Mysql8 忘记密码重置,以及问题解决

1.使用免密登录 找到配置MySQL文件&#xff0c;我的文件路径是/etc/mysql/my.cnf&#xff0c;有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...

Git常用命令完全指南:从入门到精通

Git常用命令完全指南&#xff1a;从入门到精通 一、基础配置命令 1. 用户信息配置 # 设置全局用户名 git config --global user.name "你的名字"# 设置全局邮箱 git config --global user.email "你的邮箱example.com"# 查看所有配置 git config --list…...

系统掌握PyTorch:图解张量、Autograd、DataLoader、nn.Module与实战模型

本文较长&#xff0c;建议点赞收藏&#xff0c;以免遗失。更多AI大模型应用开发学习视频及资料&#xff0c;尽在聚客AI学院。 本文通过代码驱动的方式&#xff0c;系统讲解PyTorch核心概念和实战技巧&#xff0c;涵盖张量操作、自动微分、数据加载、模型构建和训练全流程&#…...

数学建模-滑翔伞伞翼面积的设计,运动状态计算和优化 !

我们考虑滑翔伞的伞翼面积设计问题以及运动状态描述。滑翔伞的性能主要取决于伞翼面积、气动特性以及飞行员的重量。我们的目标是建立数学模型来描述滑翔伞的运动状态,并优化伞翼面积的设计。 一、问题分析 滑翔伞在飞行过程中受到重力、升力和阻力的作用。升力和阻力与伞翼面…...

沙箱虚拟化技术虚拟机容器之间的关系详解

问题 沙箱、虚拟化、容器三者分开一一介绍的话我知道他们各自都是什么东西&#xff0c;但是如果把三者放在一起&#xff0c;它们之间到底什么关系&#xff1f;又有什么联系呢&#xff1f;我不是很明白&#xff01;&#xff01;&#xff01; 就比如说&#xff1a; 沙箱&#…...