ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

spring-reading 源码实战:深入解析 BeanDefinitionHolder——BeanDefinition、Bean 名称与别名的统一封装

spring-reading 源码实战:深入解析 BeanDefinitionHolder——BeanDefinition、Bean 名称与别名的统一封装 示例工程文档【免费下载链接】spring-reading涵盖了 Spring 框架的核心概念和关键功能包括控制反转IOC容器的使用面向切面编程AOP的原理与实践事务管理的方式与实现Spring MVC 的流程与控制器工作机制以及 Spring 中数据访问、安全、Boot 自动配置等方面的深入研究。此外它还包含了 Spring 事件机制的应用、高级主题如缓存抽象和响应式编程以及对 Spring 源码的编程风格与设计模式的深入探讨。项目地址https://gitcode.com/GitHub_Trending/sp/spring-reading点击查看免费下载BeanDefinitionHolder是 Spring Frameworkbeans模块中一个不起眼却贯穿容器核心流程的类它把BeanDefinition、Bean 名称与别名打包成一个不可分割的单元是组件扫描、XML 解析、Import导入等所有 Bean 注册路径的最后一公里载体。本文以 spring-bean-beanDefinitionHolder 模块的文档为骨架结合本仓库可运行的演示代码与BeanDefinition、BeanDefinitionRegistry等关联模块的源码讲清楚它的字段语义、构造规则、matchesName匹配逻辑以及它在容器中的真实调用场景读完你可以独立读懂任何使用BeanDefinitionHolder的框架代码并能手写一个一次定义、多名称注册的 Bean 注册流程。一、知识储备读本模块前需要掌握的三个前置概念1. Spring Framework 基础知识需要先熟悉 Spring Framework 的基本概念什么是 Bean、什么是BeanDefinition以及 Spring 容器IOC 容器是如何管理和实例化 Bean 的。Bean 是容器管理的对象而BeanDefinition就是描述这个对象长什么样、怎么造、怎么毁的元数据。2.BeanDefinition的理解BeanDefinition是理解BeanDefinitionHolder的基础。它包含了 Bean 的类名、作用域、属性值、构造函数参数、初始化方法、销毁方法等全部配置信息。在本仓库的 spring-bean-beanDefinition 模块中BeanDefinition接口的源码展示了这些元数据能力的完整清单SCOPE_SINGLETON/SCOPE_PROTOTYPE作用域常量、ROLE_APPLICATION/ROLE_SUPPORT/ROLE_INFRASTRUCTURE角色提示、setBeanClassName、setLazyInit、setDependsOn、setAutowireCandidate、setInitMethodName、setDestroyMethodName等配置入口以及ConstructorArgumentValues、MutablePropertyValues两个可变的参数/属性容器。可以这么说BeanDefinition回答了这个 Bean 是什么而BeanDefinitionHolder回答了这个 Bean 叫什么、还有哪些别名。3. Spring IOC 容器需要了解 Spring 的 IOCInversion of Control容器如何配置容器、如何加载BeanDefinition、如何从容器中获取 Bean 实例。本模块的最佳实践直接使用DefaultListableBeanFactory作为容器它同时实现了BeanDefinitionRegistry接口注册表与BeanFactory接口实例工厂是理解整个注册流程的最小载体。二、基本描述BeanDefinitionHolder 是什么BeanDefinitionHolder是 Spring Framework 中的一个类用于同时持有一个BeanDefinition对象以及与之相关联的 Bean 名称和可选别名。它通常用于 Spring 内部操作在BeanDefinition从配置定义阶段进入容器注册阶段时充当包装载体方便对BeanDefinition进行标识、传递、匹配与注册。关键定位它不是一个接口而是一个具体的、不可变的包装类final字段 构造时校验不承担业务逻辑只承担定义 名称 别名三者的聚合与分发。三、主要功能持有BeanDefinition对象内部封装一份 Bean 的元数据信息类名、作用域、属性、初始化/销毁方法等通过getBeanDefinition()暴露。与名称相关联绑定一个主 Bean 名称使特定的BeanDefinition可以被轻松标识与定位通过getBeanName()暴露。包装BeanDefinition与名称将定义与名称组合成一个单元便于在注册流程中整体传递、管理与处理。提供额外的元数据除了定义与名称还能附带别名数组aliases并通过对BeanMetadataElement接口的实现getSource()透出BeanDefinition的原始来源信息通常是一个Resource对象。用于注册和管理BeanDefinition与BeanDefinitionRegistry接口协同工作在容器中注册、修改和管理BeanDefinition——典型落地就是BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry)。四、接口源码逐字段拆解BeanDefinitionHolder是 Spring 内部处理BeanDefinition注册与操作的核心载体提供了访问BeanDefinition、Bean 名称和别名的方法。以下源码与 Spring 官方实现一致完整展示其结构public class BeanDefinitionHolder implements BeanMetadataElement { // 封装的 BeanDefinition 对象 private final BeanDefinition beanDefinition; // Bean 的名称 private final String beanName; // 别名数组 Nullable private final String[] aliases; /** * 创建一个新的 BeanDefinitionHolder。 * param beanDefinition 要包装的 BeanDefinition * param beanName Bean 的名称如在 Bean 定义中指定 */ public BeanDefinitionHolder(BeanDefinition beanDefinition, String beanName) { this(beanDefinition, beanName, null); } /** * 创建一个新的 BeanDefinitionHolder。 * param beanDefinition 要包装的 BeanDefinition * param beanName Bean 的名称如在 Bean 定义中指定 * param aliases Bean 的别名如果没有别名则为 null */ public BeanDefinitionHolder(BeanDefinition beanDefinition, String beanName, Nullable String[] aliases) { Assert.notNull(beanDefinition, BeanDefinition 必须不为 null); Assert.notNull(beanName, Bean 名称必须不为 null); this.beanDefinition beanDefinition; this.beanName beanName; this.aliases aliases; } /** * 复制构造函数创建一个新的 BeanDefinitionHolder其内容与给定的 BeanDefinitionHolder 实例相同。 * 注意封装的 BeanDefinition 引用保持原样不会进行深层复制。 * param beanDefinitionHolder 要复制的 BeanDefinitionHolder 实例 */ public BeanDefinitionHolder(BeanDefinitionHolder beanDefinitionHolder) { Assert.notNull(beanDefinitionHolder, BeanDefinitionHolder 必须不为 null); this.beanDefinition beanDefinitionHolder.getBeanDefinition(); this.beanName beanDefinitionHolder.getBeanName(); this.aliases beanDefinitionHolder.getAliases(); } /** * 返回封装的 BeanDefinition。 */ public BeanDefinition getBeanDefinition() { return this.beanDefinition; } /** * 返回 Bean 的主要名称如在 Bean 定义中指定。 */ public String getBeanName() { return this.beanName; } /** * 返回 Bean 的别名数组如在 Bean 定义中直接指定。 * return 别名数组如果没有别名则为 null */ Nullable public String[] getAliases() { return this.aliases; } /** * 公开 Bean 定义的源对象。 * see BeanDefinition#getSource() */ Override Nullable public Object getSource() { return this.beanDefinition.getSource(); } /** * 确定给定的候选名称是否与存储在该 Bean 定义中的 Bean 名称或别名匹配。 */ public boolean matchesName(Nullable String candidateName) { return (candidateName ! null (candidateName.equals(this.beanName) || candidateName.equals(BeanFactoryUtils.transformedBeanName(this.beanName)) || ObjectUtils.containsElement(this.aliases, candidateName))); } // ... [部分代码省略以简化] }从源码中可以提炼出四个值得注意的实现细节三处final字段beanDefinition、beanName、aliases均不可变Holder 创建后即固定这保证了传递过程中的安全性。空值守卫构造时通过Assert.notNull强制BeanDefinition与BeanName非空只有别名允许为null。matchesName的三重匹配先与主名称beanName精确比较再通过BeanFactoryUtils.transformedBeanName处理前缀FactoryBean 解引用场景例如myBean与myBean视为同一目标最后用ObjectUtils.containsElement在别名数组中做包含判断。该方法是判断候选名称是否命中该 Bean 定义的权威入口。getSource()的透传因为实现了BeanMetadataElementHolder 把BeanDefinition.getSource()原样透出便于框架在报错或日志中定位配置来源如 XML 文件位置。五、主要实现它是具体类而非接口BeanDefinitionHolder不是接口也不需要任何实现类。它是一个具体的、开箱即用的包装类职责单一把BeanDefinition对象、Bean 名称与别名打包成一个单元以便管理和操作。它的实现体现在使用方式上——作为容器注册流程中的标准传递载体所有注册器reader、scanner、parser都以它为入参完成最后的注册动作。六、最佳实践一次定义、三个名称、同一实例本仓库的 BeanDefinitionHolderDemo.java 给出了可直接运行的完整示例使用BeanDefinitionHolder和 Spring IOC 容器先创建一个名为myBean的BeanDefinition再给它指定别名myBeanX、myBeanY最后整体注册进容器并分别用三个名称getBean验证。public class BeanDefinitionHolderDemo { public static void main(String[] args) { // 创建一个 DefaultListableBeanFactory它是 BeanDefinitionRegistry 的一个实现 DefaultListableBeanFactory beanFactory new DefaultListableBeanFactory(); // 创建一个新的 BeanDefinition 对象 GenericBeanDefinition beanDefinition new GenericBeanDefinition(); beanDefinition.setBeanClass(MyBean.class); // Bean名称 String beanName myBean; // 设置别名aliases String[] aliases {myBeanX, myBeanY}; // 创建一个 BeanDefinitionHolder将 BeanDefinition 与名称关联起来 BeanDefinitionHolder beanDefinitionHolder new BeanDefinitionHolder(beanDefinition, beanName, aliases); // 使用 BeanDefinitionReaderUtils 注册 BeanDefinitionHolder BeanDefinitionReaderUtils.registerBeanDefinition(beanDefinitionHolder, beanFactory); System.out.println(myBean beanFactory.getBean(myBean)); System.out.println(myBeanX beanFactory.getBean(myBeanX)); System.out.println(myBeanY beanFactory.getBean(myBeanY)); } }MyBean的 Java 类代表了一个简单的 Java Bean见 MyBean.javapublic class MyBean { }运行结果发现多个名称都引用同一个 Bean 定义最终获取到同一个 Bean 实例对象哈希地址一致myBean com.xcs.spring.bean.MyBean5bcea91b myBeanX com.xcs.spring.bean.MyBean5bcea91b myBeanY com.xcs.spring.bean.MyBean5bcea91b运行这段代码需要引入 Spring 的beans模块依赖。本模块的 pom.xml 继承了父模块spring-beansgroupId 为com.xcs.spring在其spring-beans父 pom 中统一管理了spring-beans等核心依赖因此示例可直接通过 Maven 编译运行。实战要点BeanDefinitionReaderUtils.registerBeanDefinition做了什么示例中的关键一步是BeanDefinitionReaderUtils.registerBeanDefinition(beanDefinitionHolder, beanFactory)它是BeanDefinitionHolder与注册表之间的桥梁。从方法语义上看它会依次完成以holder.getBeanName()为主名称调用注册表的registerBeanDefinition(beanName, beanDefinition)注册主定义遍历holder.getAliases()通过AliasRegistry.registerAlias(beanName, alias)BeanDefinitionRegistry继承自AliasRegistry把每个别名挂到主名称上注册完成后别名与主名称在容器内指向同一个BeanDefinition因此后续getBean(myBeanX)与getBean(myBean)解析到同一个单例实例。这也解释了上一节matchesName的存在意义容器在解析名称引用如depends-on、ref时需要把名称是否命中某定义的判断交给 Holder 去完成主名称、解引用名称、别名三种形态都算命中。七、与其他组件的关系BeanDefinitionReaderUtilsSpring 提供的BeanDefinition注册工具类通常与BeanDefinitionHolder配合使用。其registerBeanDefinition(BeanDefinitionHolder, BeanDefinitionRegistry)方法负责把 Holder 中的定义与别名一次性写入注册表是 Holder 最直接的消费方。BeanDefinitionParser与自定义解析器在 XML 配置解析过程中NamespaceHandler/BeanDefinitionParser体系解析器解析出BeanDefinition后会用BeanDefinitionHolder包装定义 名称 别名再交由解析器注册到容器。这正是解析与注册两个阶段之间的标准交接物。组件扫描器ClassPathBeanDefinitionScanner与AnnotatedBeanDefinitionReader在扫描类路径或注解类时会为每个候选类构造BeanDefinition并用BeanDefinitionHolder包装后注册到容器。本仓库的 spring-bean-classPathBeanDefinitionScanner 与 spring-bean-annotatedBeanDefinitionReader 模块演示了这类扫描器的完整注册流程。Import及相关机制Import注解以及ImportBeanDefinitionRegistrar、ImportSelector接口在导入其他配置类时会用BeanDefinitionHolder包装被导入的BeanDefinition有时连同别名再注册进容器。这是Configuration类中定义的方法 BeanBean方法批量注册的底层实现路径之一。八、常见问题如何使用BeanDefinitionHolderBeanDefinitionHolder通常在 Spring 内部用于BeanDefinition的注册与操作应用开发人员通常不需要直接与它交互除非你是框架开发者。它在自定义BeanDefinitionParser、组件扫描、Import导入等 Spring 配置流程中会被自动创建并使用。BeanDefinitionHolder和BeanDefinition之间的关系是什么BeanDefinitionHolder是一个包装器包含一个BeanDefinition对象以及相关的名称和别名用于将定义与名称关联起来以便更好地管理和操作BeanDefinition。如何为BeanDefinition设置别名别名通过在BeanDefinitionHolder构造函数中传递字符串数组设置new BeanDefinitionHolder(bd, myBean, new String[]{myBeanX, myBeanY})。注册 Holder 时别名会与定义一起注册使不同名称都引用同一个BeanDefinition。是否可以在运行时更改 Bean 的别名通常情况下一旦BeanDefinition注册进容器别名是固定的不能在运行时更改。如需更改需要以不同的别名重新注册BeanDefinition。如何判断两个 Bean 是否有相同的别名使用BeanDefinitionHolder的matchesName方法它会检查给定名称是否与主名称含解引用形式或任一别名匹配返回布尔结果。别名的作用是什么别名提供了灵活性允许使用不同名称引用同一个 Bean。这方便不同组件、模块或配置以各自习惯的名称访问同一个 Bean 定义也支持按名称注入时对同一实例的多路径引用。如何处理具有相同名称的多个BeanDefinition如果多个BeanDefinition使用相同名称默认情况下只有最后一个注册的定义生效具体行为取决于注册表的覆盖策略Spring 默认禁止覆盖并抛出BeanDefinitionOverrideException。因此配置时要注意 Bean 名称的唯一性避免覆盖问题。总结理解 BeanDefinitionHolder 的三句话它是元数据 标识 别名的三合一载体BeanDefinition描述 Bean 的构造蓝图beanName是主标识aliases是备选标识三者共同构成容器注册的最小完整单元。它是所有注册路径的汇聚点XML 解析BeanDefinitionParser、组件扫描ClassPathBeanDefinitionScanner、注解读取AnnotatedBeanDefinitionReader、配置导入Import/ImportBeanDefinitionRegistrar最终都借助BeanDefinitionReaderUtils将 Holder 落入BeanDefinitionRegistry。它服务于一定义多名称的语义通过matchesName支持主名称、解引用名称与别名的统一匹配让容器在按名解析时对同一实例拥有多个合法入口。想要进一步打通这条知识链建议按顺序阅读本仓库的 BeanDefinition定义元数据、BeanDefinitionRegistry注册表接口与本模块注册载体三者合起来就构成了定义 → 包装 → 注册 → 获取的完整 IOC 最小闭环。赞分享示例工程文档【免费下载链接】spring-reading涵盖了 Spring 框架的核心概念和关键功能包括控制反转IOC容器的使用面向切面编程AOP的原理与实践事务管理的方式与实现Spring MVC 的流程与控制器工作机制以及 Spring 中数据访问、安全、Boot 自动配置等方面的深入研究。此外它还包含了 Spring 事件机制的应用、高级主题如缓存抽象和响应式编程以及对 Spring 源码的编程风格与设计模式的深入探讨。项目地址https://gitcode.com/GitHub_Trending/sp/spring-reading点击查看免费下载相关推荐Spring IoC 源码剖析将 XML 中的 bean 解析封装成 BeanDefinitionSpring IoC 源码剖析将 XML 中的 bean 解析封装成 BeanDefinition 导读 本篇文章是 Spring IoC 容器初始化系列的第文档教程知识库GitHub_Trending/sp/spring-reading源码解析Spring BeanDefinitionGitHub_Trending/sp/spring reading源码解析Spring BeanDefinition 引言你真的懂Spring Bean的诞示例工程文档Figma-Linux与原生应用对比为什么Linux用户需要这个桌面版本Figma Linux与原生应用对比为什么Linux用户需要这个桌面版本 Figma Linux是专为Linux系统打造的桌面应用它将Figma的强大设计功上一篇革命性AI图像编辑模型Boogu-Image-0.1-Edit一站式解决图像编辑与生成难题下一篇5分钟上手goprocinfo从安装到获取系统负载的完整教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表