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

《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 SharedMetadataReaderFactoryContextInitializerimplements ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {// 其他省略。。。@Overridepublic void initialize(ConfigurableApplicationContext applicationContext) {BeanFactoryPostProcessor postProcessor = new CachingMetadataReaderFactoryPostProcessor(applicationContext);applicationContext.addBeanFactoryPostProcessor(postProcessor);}@Overridepublic 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.hqwc.cn/news/573121.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

架构师之路--Docker的技术学习路径

Docker 的技术学习路径 一、引言 Docker 是一个开源的应用容器引擎&#xff0c;它可以让开发者将应用程序及其依赖包打包成一个可移植的容器&#xff0c;然后在任何支持 Docker 的操作系统上运行。Docker 具有轻量级、快速部署、可移植性强等优点&#xff0c;因此在现代软件开…

ubuntu22.04系统安装Opencv4.8.0+Opencv-contrib4.8.0

一、安装下载所需工具 1.打开终端&#xff0c;输入以下命令来更新软件源&#xff1a; sudo apt-get update 2.安装wget&#xff1a; sudo apt-get install wget 3.下载opencv和opencv-contrib包&#xff1a; wget -O opencv-4.8.0.zip https://github.com/opencv/opencv/…

Vue使用el-statistic和el-card显示大屏中的统计数据

​ 一、页面内容&#xff1a; <el-row :gutter"20"><el-col :span"6"><el-card class"box-card"><div><el-statisticgroup-separator",":precision"2":value"value2":title"tit…

Flink SQL 基于Update流出现空值无法过滤问题

问题背景 问题描述 基于Flink-CDC &#xff0c;Flink SQL的实时计算作业在运行一段时间后&#xff0c;突然发现插入数据库的计算结果发生部分主键属性发生失败&#xff0c;导致后续计算结果无法插入&#xff0c; 超过失败次数失败的情况问题报错 Caused by: java.sql.BatchUp…

基于大语言模型的云故障根因分析|顶会EuroSys24论文

*马明华 微软主管研究员 2021年CCF国际AIOps挑战赛程序委员会主席&#xff08;第四届&#xff09; 2021年博士毕业于清华大学&#xff0c;2020年在佐治亚理工学院做访问学者。主要研究方向是智能运维&#xff08;AIOps&#xff09;、软件可靠性。近年来在ICSE、FSE、ATC、EuroS…

151 shell编程,正则表达式,在C语言中如何使用正则表达式

零&#xff0c;坑点记录&#xff1a;bash 和 dash 的区别&#xff0c;导致的坑点 查看当前用的shell 是啥&#xff0c;用的是/bin/bash hunandedehunandede-virtual-machine:~$ echo $SHELL /bin/bash 当shell 脚本运行的时候&#xff08;后面会学到方法&#xff0c;这里是最…

SQLite中的动态内存分配(五)

返回&#xff1a;SQLite—系列文章目录 上一篇&#xff1a;SQLite中的原子提交&#xff08;四&#xff09; 下一篇&#xff1a;SQLite使用的临时文件&#xff08;二&#xff09; ​概述 SQLite使用动态内存分配来获得 用于存储各种对象的内存 &#xff08;例如&#xff1a…

现货黄金投资锁仓是什么意思?

在金融投资领域&#xff0c;特别是黄金市场中&#xff0c;“锁仓”是一个常见的策略。那么&#xff0c;现货黄金投资中所说的“锁仓”是什么意思呢&#xff1f;简单来说&#xff0c;锁仓是投资者为了保护自己的投资不受未来价格波动的影响&#xff0c;同时锁定当前的价格水平&a…

SpringBoot整合Redis:缓存击穿--互斥锁解决

&#x1f389;&#x1f389;欢迎光临&#xff0c;终于等到你啦&#x1f389;&#x1f389; &#x1f3c5;我是苏泽&#xff0c;一位对技术充满热情的探索者和分享者。&#x1f680;&#x1f680; &#x1f31f;持续更新的专栏Redis实战与进阶 本专栏讲解Redis从原理到实践 …

flutter Got socket error trying to find package nested at

flutter Got socket error trying to find package nested at xxx 报错信息&#xff1a;“Got socket error trying to find package nested at” 通常出现在Flutter尝试从pub.dev获取依赖包时&#xff0c;由于网络问题导致无法连接到pub.dev或者无法正确解析包的路径。 例如&…

遥感卫星影像质量评价指标汇总

1. 主观评价方法 以人为图像的评价者&#xff0c;根据自己的评价尺度和经验对图像质量进行评价。 2. 客观评价方法 1)均方差 2)信噪比 主要用来评价影像经压缩、传输、增强等处理前后的质量变化情况&#xff0c;其本质与均方差类似。 3)方差 反映了图像各个像元灰度相对…

【前端面试3+1】03深拷贝浅拷贝、let和var、css盒模型、【有效括号】

一、深拷贝浅拷贝 深拷贝和浅拷贝都是用于复制对象或数组的概念&#xff0c;但它们之间有着重要的区别&#xff1a; 1. 浅拷贝&#xff1a; 浅拷贝是指在拷贝对象或数组时&#xff0c;只会复制一层对象的属性或元素&#xff0c;而不会递归地复制嵌套的对象或数组。因此&#xf…