盘点解析SpringIOC : BeanDefinition 的加载流程

【Spring源码阅读】BeanDefinition原理与加载流程 BeanDefinition接口定义及其相关子类实现 在Spring容器初始化过程中,Spring会将配置文件中配置的Java类封装成一个个BeanDefinition。 BeanDefinition存储了具体代表Bean的类,并通过实现了AttributeAccessor接口定义了读写属性配置的相关方法。在基于xml配置Spring容器中,我们为某个Bean配置了具体的属性值,这些都根据name... 阅读详情

一 . 前言

这一篇来看看 SpringIOC 里面的一个细节点 , 来简单看看 BeanDefinition 这个对象 , 以及有没有办法对其进行定制.

二. BeanDefinition 的体系

2.1 体系概览

​

 

这里面需要关注的几个类分别为 :

  • BeanDefinition 接口 : 顶层接口 , 抽象了Bean加载的方法

  • AbstractBeanDefinition : 提供了多数方法的默认实现

  • RootBeanDefinition : Spring BeanFactory 运行时统一的 BeanDefinition 视图

  • GenericBeanDefinition : 编程方式注册 BeanDefinition 的首选类

  • ChildBeanDefinition : 可继承BeanDefinition

下面来解释一下这里面说的一些概念 :

什么叫统一视图 ?

稍微从跟踪一下源码就能发现 , 从 xml 或者 JavaConfig 以及 Spring 默认加载的Bean配置类 ,最终都会被修饰为 RootBeanDefinition

GenericBeanDefinition 怎么用 ?

GenericBeanDefinition 是通过编程方式注入的 BeanDefinition 所对应的类 ,通常都是该类的子类 , 包括非Spring 的 ConfigBean 和 ServiceBean

ChildBeanDefinition 又做了什么 ?

一种可以继承 parent 配置的 BeanDefinition , 在加载环节中会通过 AbstractBeanFactory#getMergedLocalBeanDefinition() 来将 child 和 parent bean definition 进行合并。

  • BeanDefinition 进行 merge 操作时,会将 child 的属性与 parent 的属性进行合并,当有相同属性时,以 child 的为准

  • 如果是 Map 形式的配置 , 会取并集

2.2 BeanDefinition 的作用

  • 存储属性 : 基于接口 AttributeAccessor 实现

  • 存储元数据配置 : 基于 BeanMetadataElement 实现

  • 描述类的信息 : 包括Bean名称 , Primary 属性 , priority 配置 等等

  • Bean 的加载 : 例如 getBeansOfType , getBean 等等

总结其实就是一句话 : BeanDefinition 主要承载了Bean的元数据信息 ,同时描述了Bean在Spring体系中的加载方式 , 容器通过 BeanDefinition 中的配置来加载一个Bean

三. BeanDefinition 的载入

3.1 载入的入口

S1 : 启动配置类的载入

Spring 中第一个载入的 BeanDefinition 即为 RootBeanDefinition , 主要通过 AnnotationConfigUtils # registerAnnotationConfigProcessors 方法进行加载

在这个环节中会通过加载的方式分别载入多个不同的 RootBeanDefinition , 这里是 Contain 关系 :

// internalConfigurationAnnotationProcessor if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) { // 构建对应的 PostProcessor 并且载入 RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)); } // internalAutowiredAnnotationProcessor if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) { //..... } 复制代码

在这个环节中 , 基本上都是在通过 registerPostProcessor 来注册各类加载类 , 我把这些看成根类 .

这些类通常为 Spring 进行服务 , 用来配置各类信息和加载封装Bean

S2 : 普通配置类的载入

普通类的载入包括 SpringApplication 和一些自定义的个人配置类 , 这些类主要为了对非 Spring 的组件进行注册 , 配置 , 注入等操作

这一类通常通过 registerBean 来实现Bean的注册 , 注册的入口也很多 :

包括 AnnotatedBeanDefinitionReader 和 ConfigurationClassPostProcessor等, 不难发现这一类 BeanDefinition 通常都是由 RootBeanDefinition 装载的类进行载入的

通常注册出来的对象也为 AnnotatedGenericBeanDefinition 和 GenericBeanDefinition 的子类等

3.2 保存的逻辑

BeanDefinition 会在 DefaultListableBeanFactory # registerBeanDefinition 中进行注册.

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { // S1 : 会对 BeanDefinition 进行校验 , 主要是MethodOverrides和FactoryMethodName不能同时存在 // -- 工厂方法必须创建具体的 Bean 实例 , 而 methodOverrides 会创建代理类且进行增强 // -- 也就是说 工厂需要实例 , 不能是代理类 if (beanDefinition instanceof AbstractBeanDefinition) { ((AbstractBeanDefinition) beanDefinition).validate(); } // S2 : 判断 BeanDefinition 是否已经存在 BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName); if (existingDefinition != null) { // 是否允许同名Bean重写 , 因为此处已经存在一个了 , 不能重写则直接异常 if (!isAllowBeanDefinitionOverriding()) { throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition); }else if (existingDefinition.getRole() < beanDefinition.getRole()) { // 角色比较 ,只打日志 // ROLE_APPLICATION / ROLE_SUPPORT / ROLE_INFRASTRUCTURE }else if (!beanDefinition.equals(existingDefinition)) { // 判断是否为同一对象 } // 以上主要是打log , 这里如果允许覆盖则直接覆盖了 this.beanDefinitionMap.put(beanName, beanDefinition); } else { // 判断该Bean是否已经开始初始化 if (hasBeanCreationStarted()) { // 如果已经开始 , 需要对 Map 上锁后再处理 synchronized (this.beanDefinitionMap) { this.beanDefinitionMap.put(beanName, beanDefinition); // 省略一些更新操作 } } else { // 没有加载时的载入 this.beanDefinitionMap.put(beanName, beanDefinition); this.beanDefinitionNames.add(beanName); removeManualSingletonName(beanName); } this.frozenBeanDefinitionNames = null; } // 如果Bean已经存在或已经开始加载了 , 这个时候时需要进行销毁操作的 if (existingDefinition != null || containsSingleton(beanName)) { resetBeanDefinition(beanName); } } protected void resetBeanDefinition(String beanName) { // S1 : 如果已经创建 ,需要清空 Merge BeanDefinition clearMergedBeanDefinition(beanName); // S2 : 销毁 Bean destroySingleton(beanName); // S3 : 调用 PostProcessors 重置处理器进行处理 for (BeanPostProcessor processor : getBeanPostProcessors()) { if (processor instanceof MergedBeanDefinitionPostProcessor) { ((MergedBeanDefinitionPostProcessor) processor).resetBeanDefinition(beanName); } } // S4 : 如果该 BeanDefinition 是某个BeanDefinition 的Parent , 则需要同步处理 for (String bdName : this.beanDefinitionNames) { if (!beanName.equals(bdName)) { BeanDefinition bd = this.beanDefinitionMap.get(bdName); if (bd != null && beanName.equals(bd.getParentName())) { resetBeanDefinition(bdName); } } } } 复制代码

3.3 使用的方式

BeanDefinition 的批量处理流程也是在 DefaultListableBeanFactory 中进行的

public void preInstantiateSingletons() throws BeansException { List<String> beanNames = new ArrayList<>(this.beanDefinitionNames); // 对所有的 BeanDefinition 进行循环处理 for (String beanName : beanNames) { RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); // 排除掉懒加载的Bean if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { // 此处省略工厂类的判断及处理 ... // 进入Bean获取逻辑 getBean(beanName); } } //..... } 复制代码

总结

分析 BeanDefinition 不是这阶段的主要目的 , 后续会有几篇应用的文章来着重思考下如何进行业务定制

其实写源码文章是最轻松的 , 看懂就完事了 , 而写定制或者业务 , 往往写着写着发现有地方没搞懂 , 就需要回头继续看这个点 , 难度要大得多.......

附录 : BeanDefinition 功能一览

《spring设计思想》6 beanDefinition的加载实现 上面第四节讲到了spring的DI/look up的数据来源,这次分析一下spring中的beanDefinition是怎么加载到beanFactory中的。 想一想我们在平时的工作中加载spring beanDefinition的几个来源 1:xml配置文件 2:properties文件 3:注解标注 那么针对三个不同的来源,会使用不同的手段加载到spring容器中。相当于spring设计一个接口,根据不同的规则实现特殊的类。 spring设计的接口就是BeanDefinitionReader 阅读详情

相关推荐

源码深度剖析SpringBoot启动流程中开启OpenFeign的入口(ImportBeanDefinitionRegistrar详解)

是spring对外提供的动态注册beanDefinition的接口,并且spring内部大多也用该接口动态注册beanDefinition。接口的Bean不会直接注册到IOC容器,它的执行时机比较早,发生在SpringBoot启动流程的注册BeanDefinition时;接口提供了registerBeanDefinitions(AnnotationMetadata,BeanDefinitionRegistry)供子类重写;)中注册。所谓的Spring临时容器是指)中。...

m0_67403013的博客 383

盘点 SpringIOC : BeanDefinition 的加载流程

这一篇来看看 SpringIOC 里面的一个细节点 , 来简单看看 BeanDefinition 这个对象 , 以及有没有办法对其进行定制.分析 BeanDefinition 不是这阶段的主要目的 ,后续会有几篇应用的文章来着重思考下如何进行业务定制其实写源码文章是最轻松的 , 看懂就完事了 , 而写定制或者业务 , 往往写着写着发现有地方没搞懂 , 就需要回头继续看这个点 , 难度要大得多…

Huangjiazhen711的博客 680

PCIe扫盲(三)

需要特别注意的是,PCIe的Spec中明确规定只有Root有权限发起配置请求(Originate Configuration Requests),也就是说PCIe系统里面的其他设备是不允许去配置其他设备的配置空间的,即peer-to-peer的配置请求是不允许的。当配置请求进行BDF路由的时候,正是依靠这些信息来确定要找的设备的。前面的文章还介绍过,Root和Switch的每一个端口中都包含一个P2P桥,并且知道桥的配置空间头(Configuration Space Header)是Type1型的。

Liuqz2009的专栏 2406

【框架源码】Spring源码解析之BeanDefinition加载流程解析

Spring源码解析之BeanDefinition加载流程解析

互联网小阿祥 7968

宝骏kiwi 630 730 560 E100 E200 E300 310 510 610 330 530维修手册电路图资料线路图接线图更新

2020-2022年宝骏RS-3维修手册和电路图。2020-2022年宝骏RS-3维修手册和电路图。2019-2021年宝骏RC-6维修手册和电路图。2019-2021年宝骏RC-6维修手册和电路图。2019-2020年宝骏RS-5维修手册和电路图。2019-2020年宝骏RS-5维修手册和电路图。2019-2020年宝骏RM-5维修手册和电路图。2019-2020年宝骏RM-5维修手册和电路图。2019-2021年宝骏510维修手册和电路图。2019-2021年宝骏510维修手册和电路图。

weixin_45821055的博客 1774

Appstore 3.2(f)违规自救指南:从触发到申诉的完整应对策略

本文详细解析了Appstore 3.2(f)违规的应对策略,从触发原因到申诉流程,帮助开发者有效自救。文章涵盖隐瞒行为、账户关联和信息造假三大违规场景的判定逻辑,并提供申诉信写作的黄金法则和长期合规防御措施,确保开发者能够顺利通过苹果审核。

weixin_29304985的博客 664

Spring源码学习笔记(三):BeanFactory的初始化以及BeanDefinition的加载过程

应用上下文初始化简要流程一文中主要简要的描述了Spring的上下文加载的简要流程。文本是对《应用上下文初始化简要流程》的补充,主要简单的分析了BeanFactory的初始化以及BeanDefinition加载的过程. 我们知道,Spring通过refresh操作重建了ApplicaitonContext,在这个过程中同时也构建了默认的BeanFactory以及加载了BeanDefinit...

alleyz 3115

FPGA实战 —— 基于UART的多设备串口通信系统设计

本文深入探讨了基于FPGA的多设备UART串口通信系统设计。针对工业控制与物联网应用中一对多通信的需求,文章对比了共享总线式、多路独立式等硬件连接方案,并详细阐述了可配置UART IP核、波特率自适应算法以及多路数据仲裁与分发逻辑等核心设计要点。最后,通过一个四传感器采集节点的实战案例,展示了从硬件连接到Verilog代码实现的完整系统构建过程。

weixin_29172963的博客 235

从0-1了解Spring是如何运行起来的(四):BeanFactory后处理初分析,了解如何加载BeanDefinition

​ 最深刻了解一个框架的思想的方式,莫过于看源码,本系列旨在于从Springboot底层源码(Version - 2.6.6)出发,一步步了解springboot是如何运行起来的。在前述的文章中,我们主要了解了SpringBoot是如何实现配置文件的加载、context的预处理.........

Laugh_xiaoao的博客 362

Verilog HDL 100道面试题及参考答案

在 Verilog HDL 中,参数(parameter)和宏定义(define)都可用于定义常量,但它们存在诸多不同之处。参数(parameter)是模块内部的常量定义,具有局部性。它在模块内部起作用,不同模块可以有同名的参数,且值可以不同。参数通常用于定义模块的一些配置信息,例如数据位宽、延迟时间等。参数的定义和使用是在模块的作用域内进行的,通过参数传递可以方便地对模块进行配置。endmodule在上述代码中,WIDTH是一个参数,用于定义加法器的位宽。通过改变WIDTH。

大模型大数据攻城狮的专栏 1735

Python 爬虫实战:解析 JSON 数据爬取今日头条热点新闻(API 接口逆向与字段提取)

在当今信息爆炸的时代,新闻资讯的获取变得尤为重要。今日头条作为国内领先的新闻聚合平台,其热点新闻数据具有很高的价值。本文将详细介绍如何通过 Python 爬虫技术,逆向解析今日头条的 API 接口,提取并解析 JSON 数据,最终获取热点新闻信息。

yansideyucsdn的博客 3211

Spring源码01--BeanDefinition的加载流程

Spring源码01--BeanDefiniton加载流程

qq_45800365的博客 440

MinerU 本地部署完整指南:从零构建你的私有文档智能处理系统

摘要: MinerU是一款开源本地化文档解析工具,可将PDF、Word等文件智能转换为结构化Markdown,保留表格、公式、图片等元素。其核心优势包括: 隐私安全:完全离线处理,避免云端数据泄露风险; 高精度解析:基于深度学习还原文档逻辑结构,支持多格式输入; 易集成:提供RESTful API,适合金融、医疗等敏感场景。部署需Python 3.9+环境,支持CPU/GPU运行,推荐配置16GB内存及NVIDIA显卡。通过命令行或Docker即可快速启动,适用于知识库构建、RAG系统预处理等。

reset2021的博客 754

spring系列7:register

前言 前面简单介绍了Spring是什么时候加载默认的五个BeanFactory后置处理器,可能你看得云里雾里,不知其所为何用。 这篇文章也并没有解释这个五个后置处理器的作用,而是分析在this()之后的register()方法。一步步紧跟Spring步伐。 public AnnotationConfigApplicationContext(Class<?>... componentClasses) { this(); register(componentClass...

chuixue24的博客 1078

【DexGraspNet与多指手抓取算法详解】第四章 抓取检测网络架构设计

图 $G=(V, E)$ 包含两类节点:指尖节点 $V_{tip} = \{v_{tip}^i\}_{i=1}^{N_{finger}}$ 与物体表面节点 $V_{obj} = \{v_{obj}^j\}_{j=1}^{N_{surf}}$。节点特征包含位置 $x \in \mathbb{R}^3$ 、法向 $n \in \mathbb{R}^3$ 及视觉特征 $f \in \mathbb{R}^d$。

VectorShift的博客 441

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

Spring源码讲解之 registerBeanDefinitions 方法

registerBeanDefinitions 方法是在接口 org.springframework.beans.factory.xml.BeanDefinitionDocumentReader 中定义的,主要作用是:从指定的 DOM 文档中读取 bean 定义并将其注册到 bean 定义表中,该方法第一个参数是 org.w3c.dom.Document 类型(包含 bean 定义信息的 document),第二个参数是 org.springframework.beans.factory.xml.XmlRe

misterhou的专栏 4603

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

Spring的Bean定义注册中心BeanDefinitionRegistry详解

如果想注册到spring容器,让spring来完成实例化,常用方式如下:xml中通过bean节点来配置;使用@Service、@Controller、@Conponent等注解;详见Spring注解驱动开发—向Spring Ioc容器中注册Bean的7种方式。

tian830937的专栏 1353

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

Spring解析及注册BeanDefinition

上一篇文章介绍了资源文件转为Document文件. 下面接着介绍doLoadBeanDefinitions中最核心的部分.如果说前面两篇文章都是介绍XML解析的准备阶段的话,那么registerBeanDefinitions()里面会进行真正的XML文件的解析. 把代码贴一遍 protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreExcepti

qq_40708942的博客 706

Spring是如何加载BeanDefinition的

在Spring加载Bean的过程中是无法感知每个Bean的,也无法知道每一个bean具体的特征,比如某个bean是单例还是原型,是否懒加载等等。所以Spring需要beandefinition来对每个不同的bean进行描述,并不是直接把Bean直接存入容器而是读取成beanDefinition,再根据BeanDefenition对bean的描述进行实例化。那么Spring是如何读取这些BeanDefinition的呢?

weixin_51110958的博客 613

五、Spring IOC——BeanDefinition的加载

一、BeanDefinition的加载: 在Spring中,实际上是把DefaultListableBeanFactory作为一个默认的功能完整的IoC容器来使用的。我们通过编程式使用IoC容器可以更好地理解其原理: ClassPathResource res = new ClassPathResource(“beans.xml”); DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); XmlBeanDefinit

qq_35784669的博客 505

28.BeanDefinition的加载过程

28.BeanDefinition的加载过程

weixin_43860634的博客 993

深入解析Spring BeanDefinition:理解加载、解析与注册的全过程

Spring框架中的BeanDefinition是描述Bean实例的基础配置单元,负责定义Bean的各种属性和依赖。本文将深入解析Spring BeanDefinition的源码,详细介绍其加载、解析和注册过程。我们将从Spring配置文件的读取开始,逐步剖析BeanDefinition的创建、解析、以及注册到BeanFactory的全过程。

学而时习之,不亦说乎?温故而知新,可以为师矣! 1万+

spring BeanDefinition加载过程(xml)

注:1.本文内容不是很细致,只能帮助大家了解个大概流程-_-|| 2.方法里会删减无关的内容 spring beanDefinition加载,在方法obtainFreshBeanFactory中完成 refreshBeanFactory方法 createBeanFactory:创建容器,会获取parent容器作为参数传入 loadBeanDefinition:先创建beanDefinitionReader 获取resource,也就是配置的xml文件 将resource读入,解析成document

baidu_39458769的博客 679
上一篇: 看完你就能知道Java 线程栈虚拟内存在Windows 中是如何释放的
下一篇: 帮助你深入理解JVM虚拟机,什么是Java内存模型结构的方法区
Java_LingFeng
博客等级 码龄4年 505粉丝 259原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值