【Spring Boot 源码学习】共享 MetadataReaderFactory 上下文初始化器

news2024/9/29 7:20:24

《Spring Boot 源码学习系列》

在这里插入图片描述

共享 MetadataReaderFactory 上下文初始化器

  • 一、引言
  • 二、往期内容
  • 三、主要内容
    • 3.1 源码初识
    • 3.2 CachingMetadataReaderFactoryPostProcessor
      • 3.2.1 register 方法
      • 3.2.1 configureConfigurationClassPostProcessor 方法
    • 3.3 ConfigurationClassPostProcessor 的自定义供应者
    • 3.4 共享 MetadataReaderFactory 的 FactoryBean
      • 3.4.1 FactoryBean 接口
      • 3.4.2 BeanClassLoaderAware 接口
      • 3.4.3 ApplicationListener 接口
  • 四、总结

一、引言

上篇博文《深入应用上下文初始化器实现》,Huazie 带大家详细分析了 分析 Spring Boot 中预置的应用上下文初始化器实现【即 ApplicationContextInitializer 接口实现类】的源码,了解了在 Spring 容器刷新之前初始化应用程序上下文的一些具体操作。

当然其中有些实现源码比较复杂,还没有深入分析。那本篇就来对其中的
SharedMetadataReaderFactoryContextInitializer 【即 共享 MetadataReaderFactory 上下文初始化器】详细分析下。
在这里插入图片描述

二、往期内容

在开始本篇的内容介绍之前,我们先来看看往期的系列文章【有需要的朋友,欢迎关注系列专栏】:

Spring Boot 源码学习
Spring Boot 项目介绍
Spring Boot 核心运行原理介绍
【Spring Boot 源码学习】@EnableAutoConfiguration 注解
【Spring Boot 源码学习】@SpringBootApplication 注解
【Spring Boot 源码学习】走近 AutoConfigurationImportSelector
【Spring Boot 源码学习】自动装配流程源码解析(上)
【Spring Boot 源码学习】自动装配流程源码解析(下)
【Spring Boot 源码学习】深入 FilteringSpringBootCondition
【Spring Boot 源码学习】OnClassCondition 详解
【Spring Boot 源码学习】OnBeanCondition 详解
【Spring Boot 源码学习】OnWebApplicationCondition 详解
【Spring Boot 源码学习】@Conditional 条件注解
【Spring Boot 源码学习】HttpEncodingAutoConfiguration 详解
【Spring Boot 源码学习】RedisAutoConfiguration 详解
【Spring Boot 源码学习】JedisConnectionConfiguration 详解
【Spring Boot 源码学习】初识 SpringApplication
【Spring Boot 源码学习】Banner 信息打印流程
【Spring Boot 源码学习】自定义 Banner 信息打印
【Spring Boot 源码学习】BootstrapRegistryInitializer 详解
【Spring Boot 源码学习】ApplicationContextInitializer 详解
【Spring Boot 源码学习】ApplicationListener 详解
【Spring Boot 源码学习】SpringApplication 的定制化介绍
【Spring Boot 源码学习】BootstrapRegistry 详解
【Spring Boot 源码学习】深入 BootstrapContext 及其默认实现
【Spring Boot 源码学习】BootstrapRegistry 初始化器实现
【Spring Boot 源码学习】BootstrapContext的实际使用场景
【Spring Boot 源码学习】深入应用上下文初始化器实现

三、主要内容

注意: 以下涉及 Spring Boot 源码 均来自版本 2.7.9,其他版本有所出入,可自行查看源码。

3.1 源码初识

我们先来看看 SharedMetadataReaderFactoryContextInitializer 的部分源码,如下:

class SharedMetadataReaderFactoryContextInitializer
		implements ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
		
	// 其他省略。。。
	
	@Override
	public void initialize(ConfigurableApplicationContext applicationContext) {
		BeanFactoryPostProcessor postProcessor = new CachingMetadataReaderFactoryPostProcessor(applicationContext);
		applicationContext.addBeanFactoryPostProcessor(postProcessor);
	}
	
	@Override
	public int getOrder() {
		return 0;
	}
	// 其他省略。。。
}

从上述源码中,我们可以看出 SharedMetadataReaderFactoryContextInitializer 实现了 ApplicationContextInitializer<ConfigurableApplicationContext>Ordered 接口:

  • ApplicationContextInitializer<ConfigurableApplicationContext> :应用上下文初始化器接口类,有关该类的详细介绍,请查看《ApplicationContextInitializer 详解》。
  • Ordered :实现该接口可以控制应用上下文初始化器实现类的执行顺序,有关这点我们可以查看 SpringApplicationgetInitializers 方法。
    在这里插入图片描述
    在这里插入图片描述

这里排序的关键就是 spring-core 包中提供的 org.springframework.core.annotation.AnnotationAwareOrderComparator,它能够对实现了 PriorityOrdered 接口、Ordered 接口或被 @Order 注解修饰的类进行统一的排序。

我们继续查看上述 initialize 方法,可以看到这里向应用上下文中添加了一个 BeanFactoryPostProcessor【 即 CachingMetadataReaderFactoryPostProcessor】。

3.2 CachingMetadataReaderFactoryPostProcessor

我们继续查看 CachingMetadataReaderFactoryPostProcessor 的源码,如下:

在这里插入图片描述

从上述截图中,我们可以看出 CachingMetadataReaderFactoryPostProcessor 是一个静态内部类,它同时实现了 PriorityOrderedBeanDefinitionRegistryPostProcessor 接口,有关这两个接口的作用,可以查看 《深入应用上下文初始化器实现》中的 3.1.1 小节 ,这里不再赘述。

我们继续查看 postProcessBeanDefinitionRegistry 方法,发现这里调用了 register 方法 和 configureConfigurationClassPostProcessor 方法,下面一一介绍:

postProcessBeanDefinitionRegistry 方法是 BeanDefinitionRegistryPostProcessor 接口中定义的方法,它用于在标准初始化之后修改应用上下文的内部 bean 定义注册表。所有的常规 bean 定义都将已经被加载,但还没有实例化任何 bean。这允许在下一个后处理阶段开始之前添加更多的 bean 定义。

3.2.1 register 方法

首先,进入 register 方法,如下所示:

private void register(BeanDefinitionRegistry registry) {
	if (!registry.containsBeanDefinition(BEAN_NAME)) {
		BeanDefinition definition = BeanDefinitionBuilder
			.rootBeanDefinition(SharedMetadataReaderFactoryBean.class, SharedMetadataReaderFactoryBean::new)
			.getBeanDefinition();
		registry.registerBeanDefinition(BEAN_NAME, definition);
	}
}

其中 BEAN_NAME 如下截图所示:

在这里插入图片描述

register 方法逻辑简单,它的功能是检查 BeanDefinitionRegistry 中是否已存在名为 BEAN_NAMEBeanDefinition,如果不存在,则创建一个 SharedMetadataReaderFactoryBeanBeanDefinition 并将其注册到 registry 中。【有关 SharedMetadataReaderFactoryBean ,可以查看 3.4 小节】

知识点: BeanDefinitionRegistrySpring 中一个接口,它可以被看作是一个用来管理 BeanDefinition 的注册表。BeanDefinition 可以被理解为 SpringBean 的配置描述,它包含了 Bean 的元数据,如类名是否为抽象类构造函数属性值 等相关信息。这些信息将会告诉 Spring 如何创建和初始化相应的 Bean

3.2.1 configureConfigurationClassPostProcessor 方法

接着,进入 configureConfigurationClassPostProcessor 方法,可见如下截图:

在这里插入图片描述
在这里插入图片描述
configureConfigurationClassPostProcessor(BeanDefinitionRegistry) 方法中,先从 BeanDefinitionRegistry 中获取名为 org.springframework.context.annotation.internalConfigurationAnnotationProcessor 【即内部管理的 Configuration 注解处理器的 bean 名称】的 BeanDefinition;如果找不到对应的 BeanDefinition,则捕获 NoSuchBeanDefinitionException 异常后不做任何处理。

在这里插入图片描述

知识点: ConfigurationClassPostProcessorSpring 框架中的一个核心类,它实现了 BeanPostProcessor 的子接口 BeanDefinitionRegistryPostProcessor。它的主要作用是解析被 @Configuration 注解的类,并将解析到的 Bean 封装为 BeanDefinition 注册到 Spring 容器中,以供后续步骤进行统一的实例化。此外,ConfigurationClassPostProcessor 还会处理其他与配置相关的注解,如 @Component、@PropertySources、@ComponentScan、@Import等。

继续进入 configureConfigurationClassPostProcessor(BeanDefinition) 方法中:

在这里插入图片描述

从上述截图中,可以看到:

  • 如果 definitionAbstractBeanDefinition 的实例,则调用 configureConfigurationClassPostProcessor(AbstractBeanDefinition) 方法:

    • 首先,从 AbstractBeanDefinition 中获取 bean 的实例提供者 instanceSupplier
    • 接着,判断该实例提供者是否为空 ?
      • 如果不为空,则
        • 重新设置 definition 的实例提供者为 3.3 中的自定义供应者 ConfigurationClassPostProcessorCustomizingSupplier
        • 直接返回即可。
      • 如果为空,则调用 configureConfigurationClassPostProcessor(MutablePropertyValues) 方法。
  • 如果 definition 不是 AbstractBeanDefinition 的实例,则直接调用 configureConfigurationClassPostProcessor(MutablePropertyValues) 方法。

我们继续查看 configureConfigurationClassPostProcessor(MutablePropertyValues) 方法,里面只有一行代码,功能是向 propertyValues 中添加一个新的属性 "metadataReaderFactory",其值为一个指向当前上下文中名为 org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactoryBean 的引用【RuntimeBeanReference】。

知识点:

  • MutablePropertyValuesSpring 框架中的一个类,主要用于封装类属性的集合。它是一个 List 容器,包装了多个 PropertyValue 对象,每个 PropertyValue 对象封装了一个属性及其对应的值。当需要在 BeanDefinition 中修改某个类里面的属性时,就可以使用MutablePropertyValues 类。

  • RuntimeBeanReferenceSpring 框架中用于表示运行时 Bean 引用的一个对象。在 SpringBean 解析阶段,当解析器遇到需要依赖其他 Bean 的情况时,它会依据依赖 Bean 的名称创建一个RuntimeBeanReference 对象,并将这个对象放入 BeanDefinitionMutablePropertyValues 中。这个 RuntimeBeanReference 对象是对实际 Bean 的引用,它会在运行时被解析成实际的 Bean 对象。

3.3 ConfigurationClassPostProcessor 的自定义供应者

还是一样,先来看看源码:

在这里插入图片描述

ConfigurationClassPostProcessorCustomizingSupplier 是一个实现了 Supplier<Object> 接口的自定义供应者类,其包含两个成员变量:

  • ConfigurableApplicationContext context:应用上下文对象
  • Supplier<?> instanceSupplier:原始的 Supplier,用于获取ConfigurationClassPostProcessor 的实例。

通过阅读上述 get 方法,我们可以看到该类在不改变原始 Supplier 逻辑的情况下,对提供的 ConfigurationClassPostProcessor 实例重新设置了 metadataReaderFactory 属性值,而该值是通过调用 context.getBean(BEAN_NAME, MetadataReaderFactory.class)Spring 上下文中获取的一个 MetadataReaderFactoryBean 对象。

3.4 共享 MetadataReaderFactory 的 FactoryBean

话不多说,先来看看相关源码截图:

在这里插入图片描述

SharedMetadataReaderFactoryBean 也是一个静态内部类,它实现了 FactoryBean<ConcurrentReferenceCachingMetadataReaderFactory>BeanClassLoaderAwareApplicationListener<ContextRefreshedEvent> 这三个接口,下面来详细分析下:

3.4.1 FactoryBean 接口

FactoryBeanSpring 框架中用于创建复杂 Bean 的接口。它包含如下三个方法:

T getObject() throws Exception;

Class<?> getObjectType();

default boolean isSingleton() {
	return true;
}
  • getObject():该方法用于返回由 FactoryBean 创建的对象实例。这是最核心的方法,通过实现这个方法,可以定义如何创建和返回所需的对象。在 SharedMetadataReaderFactoryBean 中,该方法返回 ConcurrentReferenceCachingMetadataReaderFactory 的实例 metadataReaderFactory
  • getObjectType():该方法用于返回由 FactoryBean 创建的对象的类型,如果事先不知道,则返回 null。在 SharedMetadataReaderFactoryBean 中,该方法返回CachingMetadataReaderFactory.class,虽然实际的类型是ConcurrentReferenceCachingMetadataReaderFactory这里暂且打个问号,有清楚的朋友可以评论区讨论下
  • isSingleton():该方法用于判断由 FactoryBean 创建的对象是否为单例。如果返回 true,则表示创建的对象在 Spring IoC 容器中是单例的,即整个应用程序中只有一个实例;如果返回 false,则表示每次请求都会创建一个新的实例。在 SharedMetadataReaderFactoryBean 中,该方法返回 true

3.4.2 BeanClassLoaderAware 接口

BeanClassLoaderAwareSpring 框架中的一个 Aware 接口,它的主要作用是允许 Bean 在初始化时获取关于自身类加载器的信息,以便执行一些特定的操作,比如动态加载其他类、访问资源等。

该接口只有一个方法:

void setBeanClassLoader(ClassLoader classLoader);

SharedMetadataReaderFactoryBean 实现了该接口,并重写了 setBeanClassLoader 方法,并在该方法中,使用传入的 classLoader 来创建一个新的 ConcurrentReferenceCachingMetadataReaderFactory 实例,然后将其赋值给成员变量 metadataReaderFactory【如上 3.4 中源码截图中可见 SharedMetadataReaderFactoryBean##getObject() 返回的就是该变量】。

3.4.3 ApplicationListener 接口

监听器接口 ApplicationListener,在之前的博文已经介绍过。SharedMetadataReaderFactoryBean 实现了该接口,实现 onApplicationEvent 方法,并监听 ContextRefreshedEvent 事件。当接收到 ContextRefreshedEvent 事件时,就会回调 onApplicationEvent 方法,然后 onApplicationEvent 方法里调用 metadataReaderFactoryclearCache 方法来清除缓存。这是为了在应用上下文刷新后确保 MetadataReader 缓存是最新的。

在这里插入图片描述

四、总结

本篇 Huazie 带大家一起分析了 spring-boot-autoconfigure 子模块中预置的 应用上下文初始化器实现 SharedMetadataReaderFactoryContextInitializer 。其中涉及了很多 Spring 的知识,由于篇幅受限没有细说,大家可以查看相关 Spring 文档,并运行 Spring Boot 项目进一步加深理解。

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

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

相关文章

SpringMVC结合设计模式:解决MyBatisPlus传递嵌套JSON数据的难题

&#x1f389;&#x1f389;欢迎光临&#xff0c;终于等到你啦&#x1f389;&#x1f389; &#x1f3c5;我是苏泽&#xff0c;一位对技术充满热情的探索者和分享者。&#x1f680;&#x1f680; &#x1f31f;持续更新的专栏《Spring 狂野之旅&#xff1a;从入门到入魔》 &a…

Learn OpenGL 24 点光源阴影

点光源阴影 上个教程我们学到了如何使用阴影映射技术创建动态阴影。效果不错&#xff0c;但它只适合定向光&#xff0c;因为阴影只是在单一定向光源下生成的。所以它也叫定向阴影映射&#xff0c;深度&#xff08;阴影&#xff09;贴图生成自定向光的视角。 本节我们的焦点是…

Java进阶—GC回收(垃圾回收)

1. 什么是垃圾回收 垃圾回收(Garbage Collection&#xff0c;GC)是Java虚拟机(JVM)的一项重要功能&#xff0c;用于自动管理程序中不再使用的内存。在Java中&#xff0c;程序员不需要手动释放内存&#xff0c;因为GC会自动检测并回收不再使用的对象&#xff0c;从而减少内存泄…

Java基础【上】韩顺平(反射、类加载、final接口、抽象类、内部类)

涵盖知识点&#xff1a;反射、类加载、单例模式、final、抽象类、接口、内部类&#xff08;局部内部类、匿名内部类、成员内部类、静态内部类&#xff09; P711 反射机制原理 创建如下目录结构&#xff0c;在模块下创建src文件夹&#xff0c;文件夹要设置为Sources文件夹&…

Git使用:实现文件在不同设备之间进行同步

一、注册Gitee&#xff0c;创建远程仓库 注册网址&#xff1a;登录 - Gitee.com 打开Gitee&#xff0c;注册完进行登录&#xff0c;点击右上角【】创建一个仓库 新建仓库&#xff1a; 点击创建&#xff0c;仓库创建完毕。 二、下载Git安装包&#xff0c;并创建本地仓库 下载网…

正则表达式具体用法大全~持续更新

# 正则表达式&#xff1a; ## 单字符匹配&#xff1a; python # 匹配某个字符串&#xff1a; # text "abc" # ret re.match(b,text) # print(ret.group()) # 点&#xff08;.&#xff09;&#xff1a;匹配任意的字符(除了\n)&#xff1a; # text "\nabc&quo…

day02_mysql-DDLDMLDQL_课后练习 - 参考答案

文章目录 day02_mysql_课后练习第1题第2题 day02_mysql_课后练习 第1题 案例&#xff1a; 1、创建数据库test02_library 2、创建表格books 字段名字段说明数据类型b_id书编号int(11)b_name书名varchar&#xff08;50&#xff09;authors作者varchar(100)price价格floatpub…

【C语言】——指针四:字符指针与函数指针变量

【C语言】——指针四&#xff1a;字符指针与函数指针变量 一、字符指针二、函数指针变量2.1、 函数指针变量的创建2.2、两段有趣的代码 三、typedef关键字3.1、typedef的使用3.2、typedef与define比较 四、函数指针数组 一、字符指针 在前面的学习中&#xff0c;我们知道有一种…

FaceBook广告账号验证教程

1.登录facebook账号,点击左边的ads manager。 2.点击Create ad创建广告。 3.选择广告投放意向。 4.填写广告信息。 5.创建广告后选择付款方式&#xff0c;这里我是使用信用卡付款。这里我是使用Fomepay的虚拟卡进行绑定的。 6.填写信用卡的持卡人姓名 卡号 有效期 安全码 7.填写…

【Jenkins】群晖 配置 ssh over push 插件

群晖 配置 ssh over push 插件 前提 部署好 Jenkins 且 安装 好 ssh over push 插件 开启 群晖 ssh 服务 及 SFTP 服务 配置 Jenkins Jenkins ——系统管理——publish over ssh 测试下&#xff1a; 遇到的问题&#xff1a; jenkins.plugins.publish_over.BapPublishe…

数据容器-序列-集合-Python

师从黑马程序员 序列 序列的常用操作-切片 切片&#xff1a;从一个序列中&#xff0c;取出一个子序列 语法&#xff1a;序列[起始下标:结束下标&#xff0c;步长] 注&#xff1a;此操作不会影响序列本身&#xff0c;而是会得到一个新的序列 my_list[0.1,2,3,4,5,6] result1…

LeetCode-热题100:79. 单词搜索

题目描述 给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 单词必须按照字母顺序&#xff0c;通过相邻的单元格内的字母构成&#xff0c;其中“相邻”单元格是那些水平相…

解决长尾问题,BEV-CLIP:自动驾驶中复杂场景的多模态BEV检索方法

解决长尾问题&#xff0c;BEV-CLIP&#xff1a;自动驾驶中复杂场景的多模态BEV检索方法 理想汽车的工作&#xff0c;原文&#xff0c;BEV-CLIP: Multi-modal BEV Retrieval Methodology for Complex Scene in Autonomous Driving 链接&#xff1a;https://arxiv.org/pdf/2401.…

C++函数返回机制,返回类型

return语句终止当前正在执行的函数并将控制权返回到调用该函数的地方。 return语句有两种形式 return;return expression; 无返回值函数 没有返回值的return语句只能用在返回类型是void的函数中。 返回void的函数不要求必须有return语句&#xff0c;因为这类函数的最后一句…

手撕算法-接雨水

描述 分析 i位置能积累的雨水量&#xff0c;等于其左右两边最大高度的最小值。为了能获取i位置左右两边的最大高度。使用动态规划。两个dp数组&#xff1a; leftMaxrightMax 其中 leftMax[i] 代表i位置左边的最大高度rightMax[i] 代表i位置右边的最大高度 初始状态&#x…

新手装修:卫生间渗水原因及解决方法。福州中宅装饰,福州装修

引言 瓷砖渗水问题常常发生在卫生间区域&#xff0c;需要及时处理以免造成地面滑倒和墙面霉菌等问题&#xff0c;为了解决这一问题&#xff0c;我们应该怎么做呢&#xff1f; 首先要检查水管是否漏水&#xff0c;可以进行打压测试来确认是否存在漏水情况。如果发现水管破损造成…

php 快速入门(一)

一、配置系统环境 1.1 安装软件 1、安装php的开发软件&#xff1a;phpstorm 在这个软件中写代码 2、安装php的运行软件&#xff1a;phpstduy 写好的php程序需要放到phpstduy中&#xff0c;用户才能访问和测试 安装过程注意事项&#xff1a;安装的路径中不能有空格和中文字符&…

什么是 PDAF?它是如何工作的?相位检测自动对焦解释

常见问题解答 什么是相位对焦 PDAF 代表相位检测自动对焦。这是一种自动对焦方法,可以检测光线进入相机时的行进和交汇位置。在智能手机中,这是在传感器级别完成的。为了使物体聚焦,光线应该在同一点相遇。如果不这样做,系统将确定如何调整镜头以达到焦点。 PDAF 好用吗…

HTTP --- 下

目录 1. HTTP请求方式 1.1. HTML 表单 1.2. GET && POST方法 1.2.1. 用 GET 方法提交表单数据 1.2.2. 用 POST 方法提交表单数据 1.2.3. 总结 1.3. 其他方法 2. HTTP的状态码 2.1. 重定向 2.1.1. 临时重定向 && 永久重定向 2.1.2. 302 &&…

UE5 C++ 3D血条 响应人物受伤 案例

一.3Dwidget 1.创建C Userwidget的 MyHealthWidget&#xff0c;声明当前血量和最大血量 UCLASS() class PRACTICEC_API UMyHealthWidget : public UUserWidget {GENERATED_BODY() public:UPROPERTY(EditAnywhere,BlueprintReadWrite,Category "MyWidget")float C…