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

news2024/9/21 3:37:04

目录

前言

通过前文:《深入分析-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
	 */
	@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 -> {
					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.coloradmin.cn/o/1381736.html

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

相关文章

美力AI变革:生成式AI在美妆和时尚领域的巨大改变

美妆AI技术解决方案提供商—玩美移动于今日发布最新全球趋势报告&#xff1a;《生成式AI在美妆和时尚领域的巨大改变》&#xff0c;就生成式AI在美妆和时尚行业的崛起&#xff0c;为品牌商提供了富有洞见的深入分析。该报告分析了来自玩美移动屡获殊荣的玩美系列APP应用套件的大…

Linux Bonding 技术解析与配置指南

介绍 在复杂的网络环境中&#xff0c;为了提高带宽、负载均衡和冗余备份&#xff0c;Linux 提供了 Bonding 技术。Bonding 技术允许将多个物理网络接口绑定在一起&#xff0c;形成一个逻辑接口&#xff0c;以提高网络性能和可用性。 Bonding 七种模式 Linux Bonding 支持多种模…

jar包部署到linux虚拟机的docker中之后连不上mysql

前言&#xff1a; 跟着黑马学习docker的时候&#xff0c;将java项目部署到了docker中&#xff0c;运行访问报错&#xff0c;反馈连不上mysql。 错误描述&#xff1a; 方法解决&#xff1a; 概述&#xff1a;在虚拟中中&#xff0c;我进入项目容器的内部&#xff0c;尝试ping…

软件测试|web自动化测试神器playwright教程(三十八)

简介 在我们使用selenium时&#xff0c;我们可以获取元素的属性&#xff0c;元素的文本值&#xff0c;以及输入框的内容等&#xff0c;作为比selenium更为强大的web自动化测试神器&#xff0c;playwright也可以实现对元素属性&#xff0c;文本值和输入框内容的抓取&#xff0c…

Docker五部曲之二:Docker引擎

文章目录 前言Docker引擎镜像管理容器管理容器运行前台运行和后台运行容器识别重启策略清除 容器日志 数据管理卷挂载创建和管理卷启动带有卷的容器通过Docker Compose使用卷使用只读卷备份、恢复和迁移卷 绑定挂载用绑定挂载启动一个容器Docker Compose使用绑定挂载使用只读绑…

AcWing 103. 电影(map、pair连用or离散化)

题目 方法一&#xff08;mappair&#xff09; 其实上面这么长巴拉巴拉就是在说 首先&#xff0c;每个科学家会的语言都不同。但是呢每部电影的字幕和语言是不一样的&#xff08;字幕和语言一定不相同&#xff09; 要求找到一部电影使得在场能听懂的科学家最多&#xff08;如果存…

JVM基础(10)——老年代调优

作者简介&#xff1a;大家好&#xff0c;我是smart哥&#xff0c;前中兴通讯、美团架构师&#xff0c;现某互联网公司CTO 联系qq&#xff1a;184480602&#xff0c;加我进群&#xff0c;大家一起学习&#xff0c;一起进步&#xff0c;一起对抗互联网寒冬 学习必须往深处挖&…

MySQL数据库入门到大牛_高级_00_MySQL高级特性篇的内容简介

文章目录 一、整个MySQL的思维导图二、MySQL高级特性篇大纲1. MySQL架构篇2. 索引及调优篇3. 事务篇4. 日志与备份篇 一、整个MySQL的思维导图 下图为整个MySQL内容&#xff0c;01-05是基础篇&#xff0c;06-09是高级篇 二、MySQL高级特性篇大纲 MySQL高级特性分为4个篇章&…

2.4G水墨屏电子标签|RFID电子纸基站CK-RTLS0501G_VT硬件功能与联机方法

2.4G水墨屏|RFID电子纸基站CK-RTLS0501G_VT是基于2.4G无线技术的可视化标签基站&#xff0c;支持10个通道范围根据使用环境情况来设定使用通道能有效避免干扰的情况。该基站支持对可视化标签进行固件及显示模板的更新等功能&#xff0c;更好集中管理可视化标签。可广泛应用于制…

AI文本生图模型Stable Diffusion部署教程

本文基于CentOS8进行Stable Diffusion开源框架部署. 1. DNS配置(但是今天出现了偶尔无法下载问题) 为了加速Github访问,我在本机配置如下 (sd) [rootshenjian stable-diffusion-webui]# cat /etc/hosts 127.0.0.1 shenjian localhost localhost.localdomain localhost4 loca…

VMware虚拟机安装银河麒麟桌面版V10系统

1镜像下载 1.1打开官网 百度搜索或浏览器地址栏直接输入地址 1.2找到镜像下载 选择桌面版操作系统进行安装我们日常办公使用的是桌面版的系统,选择桌面版操作系统进行下载 1.3申请试用 现在安装系统需要填写申请试用表,填写完之后,才会调转到镜像下载随着国产系统的日益…

QT基础篇(1)QT概述

1.什么是QT QT是一个跨平台的C应用程序开发框架。它提供了一套丰富的图形用户界面&#xff08;GUI&#xff09;和多媒体功能&#xff0c;可以用于开发各种类型的应用程序&#xff0c;包括桌面应用程序、移动应用程序和嵌入式系统。QT具有易于使用、可定制性强、性能高等特点&a…

C语言变量与函数

目录 变量函数 变量 变量&#xff1a;计算机里的一块内存空间int a 0; 表示定义一个整型 int 变量&#xff1b;这个变量名字叫做 a “” 表示赋值&#xff1b;即将右边的 0 赋值给左边的整型变量 a 现在这一块空间 a 存放了一个值 0 这个过程也叫做整型变量 a 的初始化初始化…

Web后端开发

一、Maven 1.1 简介 1.2 作用 1.3 流程 通过各种插件实现项目的标准化构建。 1.4 安装 1.5 配置环境 1.5.1 当前工程环境 1.5.2 全局环境 1.6 创建 Maven项目 1.7 导入项目 1.8 依赖管理 1.8.1 依赖配置 1.8.2 依赖传递 pom.xml——右键——Diagrams——show dependen…

【物联网】物联网设备和应用程序涉及协议的概述

物联网设备和应用程序涉及协议的概述。帮助澄清IoT层技术栈和头对头比较。 物联网涵盖了广泛的行业和用例&#xff0c;从单一受限制的设备扩展到大量跨平台部署嵌入式技术和实时连接的云系统。 将它们捆绑在一起是许多传统和新兴的通信协议&#xff0c;允许设备和服务器以新的&…

飞鱼CRM接入第三方系统 飞鱼API对接详细教程

场景描述 在白码低代码开发平台中&#xff0c;是支持外部crm系统的线索通过接口流入到白码系统里面&#xff0c;换而言之&#xff0c;只要外部的系统有线索api接口&#xff0c;白码系统可以接收线索并在白码系统上进行后续操作。本文以飞鱼crm系统为例&#xff0c;讲解如何接收…

Linux高性能服务器编程——学习笔记①

第一章、tcp/ip协议族 一、tcp/ip协议族1.1 主要的协议1.1.1 数据链路层1.1.2 网络层1.1.3 传输层1.1.4 应用层 1.2 封装1.3 分用1.4 测试网络1.5 ARP协议工作原理1.5.1 以太网ARP请求/应答报文详解1.5.2 ARP高速缓存的查看和修改1.5.3 使用tcpdump观察ARP通信过程 1.6 DNS工作…

CAN总线通信详解 (超详细配34张高清图)

CAN总线通信详解 (超详细配34张高清图) 1. CAN总线历史 CAN 是 Controller Area Network 的缩写&#xff08;以下称为 CAN&#xff09;&#xff0c;是 ISO国际标准化的串行通信协议。 在当前的汽车产业中&#xff0c;出于对安全性、舒适性、方便性、低公害、低成本的要求&#…

慕尼黑工业大学最新提出!单目实时密集建图的混合隐式场方法

作者&#xff1a;小柠檬 | 来源&#xff1a;3DCV 在公众号「3DCV」后台&#xff0c;回复「原论文」可获取论文pdf 我们提出了一种新颖的方法&#xff0c;它将基于深度学习的密集SLAM与神经隐式场相结合&#xff0c;实时生成密集地图&#xff0c;而无需像以前的方法那样依赖RGB-…

如何确定测试用例的优先级?5个方面

测试用例优先级的确定&#xff0c;有助于测试团队合理分配测试资源&#xff0c;集中精力测试重点功能和场景&#xff0c;确保重点功能测试用例的覆盖率&#xff0c;从而有利于快速发现和解决重点功能缺陷&#xff0c;确保项目进度和质量。 如果测试用例没有明确的优先级&#x…