请你谈谈:AnnotatedBeanDefinitionReader 显式地注册一个Bean到Spring容器,以及注册并解析配置类

为了深入探讨Spring框架中的beanDefinition对象,我们不可避免地要提及BeanFactoryPostProcessor这一核心类,它作为Spring的bean工厂后置处理器发挥着关键作用。接下来,我们将详细讨论BeanFactoryPostProcessor的执行时机,这是一个至关重要的环节,且Spring内部的处理机制相对复杂。本文旨在重点分析Spring如何确保这些执行时机得以严谨地遵循,并探讨如何扩展Spring的bean工厂后置处理器功能。
首先,通过一张图表,我们可以直观地理解Spring容器在启动过程中调用BeanFactoryPostProcessor后置处理器时的方法执行顺序。这将有助于我们更加清晰地把握Spring框架内部的工作流程和机制。
在这里插入图片描述

在这里插入图片描述
上述图示主要概述了Spring框架在调用BeanFactoryPostProcessor时的四个关键步骤(需要注意的是,这里仅聚焦于Spring如何触发BeanFactoryPostProcessor的调用,而并未涵盖Spring容器启动的全部流程)。
第一步,启动main方法,并在该方法中触发Spring容器的初始化。
第二步,调用AnnotationConfigApplicationContext的无参数构造函数。在这一步中,框架会首先实例化AnnotatedBeanDefinitionReader对象。这一步骤具有两个核心意义:

	public AnnotationConfigApplicationContext() {
		StartupStep createAnnotatedBeanDefReader = this.getApplicationStartup().start("spring.context.annotated-bean-reader.create");
		this.reader = new AnnotatedBeanDefinitionReader(this);
		createAnnotatedBeanDefReader.end();
		this.scanner = new ClassPathBeanDefinitionScanner(this);
	}

1.关于AnnotatedBeanDefinitionReader的用途(问题一)
2.实例化AnnotatedBeanDefinitionReader的过程(问题二)

接下来,Spring在②-2步骤中实例化了一个ClassPathBeanDefinitionScanner对象。这个对象通常用于执行Spring的类路径扫描功能,但值得注意的是,Spring在实际执行扫描操作时并未直接使用这个实例化的对象。相反,Spring在扫描的过程中会重新创建一个新的ClassPathBeanDefinitionScanner对象实例(我们暂且称之为b)。这里存在两个相同类型的对象(尽管它们都是ClassPathBeanDefinitionScanner的实例,但我们可以区分它们为a和b),这一设计有其特定的原因。为何需要两个ClassPathBeanDefinitionScanner对象实例(问题三)?至于问题三:如果Spring在内部扫描时未使用实例a,那么它为何会被创建?第②步完成了必要的准备和配置工作,为后续的扫描过程奠定了基础。

在步骤③中,register(Appconfig.class)方法被调用,它首先将Appconfig类解析为一个BeanDefinition对象。这一解析过程实际上是依赖于之前提到的AnnotatedBeanDefinitionReader对象,该对象负责将带有注解的类转化为Spring容器能够理解的BeanDefinition表示。随后,该BeanDefinition对象会被赋予一些默认的属性设置,并放置到BeanDefinitionMap中。

为何需要将BeanDefinition放入BeanDefinitionMap中呢?这主要是为了方便Spring容器后续的管理和检索。在Spring容器的生命周期中,它会遍历这个Map,并根据其中的BeanDefinition来实例化相应的Bean。对于Appconfig类而言,由于其包含多个带有@Bean注解的方法,这些方法的执行依赖于Appconfig实例本身,因此Appconfig的BeanDefinition必须存在于Map中,以确保它能够被实例化为一个Bean。关于Appconfig的实例化过程,它比一般的Bean实例化要复杂得多,因为它可能涉及到代理和CGLIB等技术的使用,这些将在后续的文章中详细解释。

至于为何Appconfig类是通过register(Appconfig.class)方法手动添加到Map中而不是通过扫描,这是因为Appconfig类自身无法被自己的扫描器所扫描到。在Spring中,一般的类是通过解析配置类(如Appconfig)上的@ComponentScan注解,然后由扫描器自动发现并添加到BeanDefinitionMap中的。但由于Appconfig是配置类本身,它无法扫描自己,因此需要通过手动调用的方式将其注册到容器中。

接下来是步骤④,其中包括了从④-1到④-4的一系列操作,而④-5则是本文重点讨论的,即执行Spring中的BeanFactoryPostProcessor。这个步骤是Spring容器启动过程中的关键一环,用于在Bean实例化之前对Bean的定义进行后处理

AnnotatedBeanDefinitionReader这个类的核心作用主要体现在以下两个方面:

  1. 编程式动态注册带注解的Bean:假设我们有一个位于com.shadow包下的类A,并且该类上标注了如@Component这样的注解。在标准配置下,Spring容器会通过扫描机制自动发现并注册此类。然而,在某些特定场景下,Spring可能无法扫描到com.shadow包,导致类A无法被容器实例化。这些特定场景包括但不限于:类A是动态生成的,在容器启动时尚不存在;或者com.shadow包下存在大量类,但仅有个别类(如类A)带有注解,此时通过扫描整个包来发现这些类显得效率低下。此时,我们可以利用AnnotatedBeanDefinitionReader的register(Class<?>clazz)方法,将类A显式地注册到Spring容器中。这个过程实际上是将类A解析为BeanDefinition对象,并将其添加到Spring的BeanDefinitionMap中,从而确保类A能够被容器正常实例化和管理。

为了验证这一功能,我们可以编写一个简单的示例,通过AnnotatedBeanDefinitionReader手动注册一个带有@Component注解的类,并观察其在Spring容器中的行为。
在这里插入图片描述
在这里插入图片描述

那么,除了能够动态显式注册一些Spring在扫描过程中可能遗漏的类之外,AnnotatedBeanDefinitionReader还有哪些功能呢?在初始化Spring容器的过程中,它究竟扮演了怎样的角色?或者说,如果没有需要动态显式注册的类,它是否就失去了存在的意义?此外,AnnotatedBeanDefinitionReader对象的应用场景仅限于注册自定义类吗?——答案是否定的。它还有一个重要应用场景,那就是注册我们的配置类。对于不太了解配置类的读者,可以简单理解为那些标注了@Configuration和@ComponentScan注解的类,例如常见的Appconfig.java。这类配置类通过AnnotatedBeanDefinitionReader注册到Spring容器中,起到了配置和管理Bean的作用。

@Configuration
@ComponentScan("com.springIoc.config")
public class AppConfig {
}

那么,为何配置类需要手动注册呢?这主要是因为配置类在Spring的自动扫描机制中具有一定的特殊性。在Spring框架中,配置类(如Appconfig.java)往往包含了@ComponentScan注解,该注解用于指定需要被扫描的包路径。然而,由于Spring的扫描过程需要依赖于配置类中的@ComponentScan注解来确定扫描范围,因此配置类本身在扫描开始之前就必须被Spring容器所知晓。

具体来说,当Spring启动并尝试进行自动扫描时,它首先需要解析配置类(如Appconfig.java)中的@ComponentScan注解,以获取需要扫描的包名。然后,Spring会根据这个包名去扫描该包下的所有Bean定义。由于配置类本身包含了这些关键信息,因此它必须在一开始就手动注册给Spring容器。

一旦Spring获得了配置类的Class对象(如Appconfig.class),它就会将其解析为一个BeanDefinition对象。这个BeanDefinition对象包含了配置类的所有元数据,包括@ComponentScan注解的值。之后,Spring就会根据这个BeanDefinition对象中的信息去扫描指定的包路径,从而发现和注册其他Bean。

因此,配置类需要手动注册的原因在于其包含了扫描过程中所需的关键信息,这些信息在扫描开始之前就必须被Spring容器所获取。

作用:

  1. 动态注册Bean:AnnotatedBeanDefinitionReader的主要功能之一是能够动态、明确地注册一个Bean到Spring容器中。这允许开发者在运行时根据需求灵活添加Bean定义。
  2. 类解析功能:除了注册功能外,它还具备解析类的能力。这种解析能力与Spring框架中用于扫描和解析类的机制相似,能够处理带有注解的类,并将其转换为Spring容器可识别的Bean定义。

应用场景:

  1. 程序员提供的Bean注册:在开发过程中,当程序员需要显式地注册一个Bean到Spring容器中时,可以使用AnnotatedBeanDefinitionReader。这种方式尤其适用于那些不在Spring的自动扫描路径下,或者需要特殊配置的Bean。
  2. 配置类的注册与解析:在初始化Spring容器的过程中,AnnotatedBeanDefinitionReader扮演着关键角色。它负责注册并解析配置类(如标注了@Configuration和@ComponentScan的类)。这些配置类通常包含了Bean的定义和扫描路径的配置,是Spring容器构建过程中不可或缺的部分。通过AnnotatedBeanDefinitionReader,这些配置类能够被正确地加载和解析,从而确保Spring容器能够按照预期的方式运行。

针对AnnotatedBeanDefinitionReader应用场景的第二点,关于其在Spring容器初始化过程中的应用,我将进一步详细阐述。通常,程序员在初始化Spring容器时,会采用多种不同的代码实现方式,但核心原理是相似的。以下,我将列举两种常见的示例来说明这一点。
第一种写法:

AnnotationConfigApplicationContext ac =new AnnotationConfigApplicationContext();
// 动态注册一个配置类
ac.register(Appconfig.class);
// 调用refresh方法
// 这里很多资料都叫做刷新spring容器,但是我觉得不合适,这是硬核翻译,比较生硬。笔者觉得理解为初始化spring容器更加精准,至于为什么以后慢慢更新再说
ac.refresh();

第二种写法:

AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Appconfig.class);

实际上,虽然两种方法都能获取到AnnotationConfigApplicationContext(简称AC对象),但推荐第一种写法的原因在于其提供了更大的灵活性和控制力。首先,第二种方法是在Spring容器完成初始化后才获取AC对象,此时容器的配置和状态已经固定,限制了后续能进行的操作。而第一种方法允许在容器初始化之前获取AC对象,从而能够执行更多的定制化和配置操作。

具体来说,①在容器初始化之前,我们可以动态地注册自定义的Bean,这正是AnnotatedBeanDefinitionReader的应用场景之一。②此外,我们还可以利用AC对象来管理和控制Spring的循环依赖特性,这在某些复杂的应用场景中尤为关键。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

人工智能 (AI) 应用:一个高精度ASD 诊断和照护支持系统

自闭症谱系障碍&#xff08;ASD&#xff09;是一种多方面的神经发育状况&#xff0c;影响全球大约1/100的儿童&#xff0c;而在中国&#xff0c;这一比例高达1.8%&#xff08;引用自《中国0&#xff5e;6岁儿童孤独症谱系障碍筛查患病现状》&#xff09;&#xff0c;男童为2.6%…

ns3-gym入门(三):在opengym基础上实现一个小小的demo

因为官方给的"opengym""opengym-2"这两个例子都很简单&#xff0c;所以自己改了一个demo&#xff0c;把reward-action-state相互影响的关系表现出来 一、准备工作 在ns3.35/scratch目录下创建一个文件夹&#xff1a; &#xff08;后续的运行指令后面都需要…

C++字体库开发之字符显示四

freetype提取路径&#xff0c;转svg显示 std::string FontPath::toSvg(const Segment &seg) const {if (seg.pts.empty())return "";std::ostringstream strStream;for (const auto &pt : seg.pts) {if (!strStream.view().empty())strStream << &quo…

【linux】服务器重装系统之系统盘写入准备

【linux】服务器重装系统之系统盘写入准备 【创作不易&#xff0c;求点赞关注收藏】&#x1f600; 文章目录 【linux】服务器重装系统之系统盘写入准备一、前期准备1、准备一个U盘&#xff0c;并进行格式化2、下载UltralSO工具3、下载对应的Ubuntu版本 二、写入操作教程 一、…

gorm多表联合查询 Joins方法 LEFT JOIN , RIGHT JOIN , INNER JOIN, FULL JOIN 使用总结

gorm中多表联合查询&#xff0c;我们可以使用Joins来完成&#xff0c;这个Joins方法很灵活&#xff0c;我们可以非常方便的多多表进行联合查询&#xff0c; 我们先来看看这个方法的官方定义和使用示例&#xff1a; Joins方法定义和使用示例 当然我们这里要说的使用方式是官方示…

nginx生成自签名SSL证书配置HTTPS

一、安装nginx nginx必须有"--with-http_ssl_module"模块 查看nginx安装的模块&#xff1a; rootecs-7398:/usr/local/nginx# cd /usr/local/nginx/ rootecs-7398:/usr/local/nginx# ./sbin/nginx -V nginx version: nginx/1.20.2 built by gcc 9.4.0 (Ubuntu 9.4.0…

Vue.js 中的 immediate: true的作用

在使用 Vue.js 时&#xff0c;监听器 (watchers) 是一种非常重要的工具&#xff0c;它允许我们观察和响应数据的变化。 immediate: true 的作用 默认情况下&#xff0c;监听器只有在所监视的数据属性发生变化时才会触发回调函数。然而&#xff0c;有时候我们需要在组件初始化时…

Hadoop-29 ZooKeeper集群 Watcher机制 工作原理 与 ZK基本命令 测试集群效果 3台公网云服务器

章节内容 上节我们完成了&#xff1a; ZNode的基本介绍ZNode节点类型的介绍事务ID的介绍ZNode实机测试效果 背景介绍 这里是三台公网云服务器&#xff0c;每台 2C4G&#xff0c;搭建一个Hadoop的学习环境&#xff0c;供我学习。 之前已经在 VM 虚拟机上搭建过一次&#xff…

LVS+Nginx高可用集群---keepalived原理与实战

1.高可用集群架构keepalived双机主备原理 高可用&#xff1a;(HA) 部署nginx存在两台nginx。当主节点的nginx宕机停止服务的时候&#xff0c;nginx备用机起到跟nginx(主) keepalived的概念&#xff1a;解决单点故障&#xff1b;组件免费&#xff1b;可以实现高可用HA机制&…

《0基础》学习Python——第十一讲

一、lambda 匿名函数 lambda函数是一种匿名函数。它是一种快速定义单行函数的方法。与常规函数不同&#xff0c;lambda函数没有名称&#xff0c;也没有使用def关键字来定义。lambda函数通常用于一些简单的函数&#xff0c;可以在代码中快速定义和使用&#xff0c;而不需要为其定…

Hive的基本操作(查询)

1、基础查询 基本语法 select 字段列表|表达式|子查询 from 表(子查询|视图|临时表|普通表) where [not] 条件A and|or 条件B --先&#xff1a;面向原始行进行筛选 group by 字段A[,字段B,...] > 分组【去重处理】 having 聚合条件(非原始字段条件) --再&#x…

《梦醒蝶飞:释放Excel函数与公式的力量》12.3 DMIN函数

第12章&#xff1a;数据库函数 第三节 12.3 DMIN函数 12.3.1 简介 DMIN函数是Excel中的一个数据库函数&#xff0c;用于返回数据库或数据表中特定条件下某字段的最小值。DMIN函数在处理大规模数据、数据筛选和分析时非常有用。 12.3.2 语法 DMIN(database, field, criteri…

MYSQL 四、mysql进阶 9(数据库的设计规范)

一、为什么需要数据库设计 二、范 式 2.1 范式简介 在关系型数据库中&#xff0c;关于数据表设计的基本原则、规则就称为范式。 可以理解为&#xff0c;一张数据表的设计结 构需要满足的某种设计标准的 级别 。要想设计一个结构合理的关系型数据库&#xff0c;必须满足一定的…

LLM量化--AWQ论文阅读笔记

写在前面&#xff1a;近来大模型十分火爆&#xff0c;所以最近开启了一波对大模型推理优化论文的阅读&#xff0c;下面是自己的阅读笔记&#xff0c;里面对文章的理解并不全面&#xff0c;只将自己认为比较重要的部分摘了出来&#xff0c;详读的大家可以参看原文 原论文地址&am…

Leetcode—146. LRU 缓存【中等】(shared_ptr、unordered_map、list)

2024每日刷题&#xff08;143&#xff09; Leetcode—146. LRU 缓存 先验知识 list & unordered_map 实现代码 struct Node{int key;int value;Node(int key, int value): key(key), value(value) {} };class LRUCache { public:LRUCache(int capacity): m_capacity(capa…

axios以post方式提交表单形式数据

某些后端框架请求接口必须走form表单提交的那种形式&#xff0c;但前端很少有<form action"接口地址" method"post"></form>这种写法去提交表单数据&#xff0c;所以前端需要用axios模拟一个表单提交接口。 Content-Type 代表发送端&#xff0…

【.NET全栈】ASP.NET开发web应用——ASP.NET中的样式、主题和母版页

文章目录 前言一、在ASP.NET中应用CSS样式1、创建CSS样式&#xff08;1&#xff09;内联样式&#xff08;2&#xff09;内部样式表&#xff08;3&#xff09;外部样式表 2、应用CSS样式&#xff08;1&#xff09;菜鸟教程-简单例子&#xff08;2&#xff09;菜鸟教程-用户界面&…

零售门店收银系统源码

php收银系统源码-CSDN博客文章浏览阅读268次&#xff0c;点赞6次&#xff0c;收藏4次。收银系统源码https://blog.csdn.net/qh716/article/details/140431477 1.系统开发语言 核心开发语言: PHP、HTML5、Dart后台接口: PHP7.3后合管理网站: HTML5vue2.0element-uicssjs线下收…

【区块链 + 智慧政务】涉税行政事业性收费“e 链通”项目 | FISCO BCOS应用案例

国内很多城市目前划转至税务部门征收的非税收入项目已达 17 项&#xff0c;其征管方式为行政主管部门核定后交由税务 部门征收。涉税行政事业性收费受限于传统的管理模式&#xff0c;缴费人、业务主管部门、税务部门、财政部门四方处于 相对孤立的状态&#xff0c;信息的传递靠…

校园网自动登录脚本【Windows 10】

如果要使用校园网&#xff0c;必须打开浏览器输入校园网地址&#xff0c;之后输入账号密码登录。实验室电脑绝大多数情况下应该处于联网状态&#xff0c;但不幸的是&#xff0c;我深会限制校园网客户端数量&#xff0c;一旦有新设备接入&#xff0c;很可能实验室电脑就会断网。…