【Spring Boot 源码学习】ConditionEvaluationReport 日志记录上下文初始化器

news2024/11/29 2:51:35

《Spring Boot 源码学习系列》

在这里插入图片描述

ConditionEvaluationReport 日志记录上下文初始化器

  • 一、引言
  • 二、往期内容
  • 三、主要内容
    • 3.1 源码初识
    • 3.2 ConditionEvaluationReport 监听器
    • 3.3 onApplicationEvent 方法
    • 3.4 条件评估报告的打印展示
  • 四、总结

一、引言

上篇博文《共享 MetadataReaderFactory 上下文初始化器》,Huazie 带大家详细分析了
SharedMetadataReaderFactoryContextInitializer 。而在 spring-boot-autoconfigure 子模块中预置的上下文初始化器中,除了共享 MetadataReaderFactory 上下文初始化器,还有一个尚未分析。

那么本篇就来详细分析一下 ConditionEvaluationReportLoggingListener 【即 ConditionEvaluationReport 日志记录上下文初始化器】。

在这里插入图片描述

二、往期内容

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

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 源码学习】深入 ApplicationContext 初始化器实现
【Spring Boot 源码学习】共享 MetadataReaderFactory 上下文初始化器

三、主要内容

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

3.1 源码初识

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

public class ConditionEvaluationReportLoggingListener
		implements ApplicationContextInitializer<ConfigurableApplicationContext> {

	private final Log logger = LogFactory.getLog(getClass());

	private ConfigurableApplicationContext applicationContext;

	private ConditionEvaluationReport report;

	private final LogLevel logLevelForReport;

	public ConditionEvaluationReportLoggingListener() {
		this(LogLevel.DEBUG);
	}

	public ConditionEvaluationReportLoggingListener(LogLevel logLevelForReport) {
		Assert.isTrue(isInfoOrDebug(logLevelForReport), "LogLevel must be INFO or DEBUG");
		this.logLevelForReport = logLevelForReport;
	}
	
	// 省略。。。

	@Override
	public void initialize(ConfigurableApplicationContext applicationContext) {
		this.applicationContext = applicationContext;
		applicationContext.addApplicationListener(new ConditionEvaluationReportListener());
		if (applicationContext instanceof GenericApplicationContext) {
			// Get the report early in case the context fails to load
			this.report = ConditionEvaluationReport.get(this.applicationContext.getBeanFactory());
		}
	}
	
	// 省略。。。
}

从上述源码中,我们可以看出 ConditionEvaluationReportLoggingListener 实现了 ApplicationContextInitializer<ConfigurableApplicationContext> 【即应用上下文初始化器接口】,有关 ApplicationContextInitializer 的详细介绍,请查看《ApplicationContextInitializer 详解》。

它有三个成员变量,分别是:

  • ConfigurableApplicationContext applicationContext : 应用上下文对象
  • ConditionEvaluationReport report :条件评估报告对象,用于报告和记录条件评估详细信息。
  • LogLevel logLevelForReport :条件评估报告的日志级别,包含 TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF

再来看看构造方法,它有两个:

  • 无参构造方法:初始化日志级别为 DEBUG【默认通过它实例化该上下文初始化器】
  • LogLevel 参数的构造方法Assert.isTrue 是用于验证一个条件是否为真。通过 isInfoOrDebug 来判断日志级别参数 logLevelForReport 是否是 INFODEBUG 级别,如果不是,则会抛出一个 IllegalArgumentException 异常并显示错误信息 “LogLevel must be INFO or DEBUG”

我们继续查看 initialize 方法,可以看到 :

  • 首先,初始化成员变量应用上下文对象 applicationContext,便于后续使用。
  • 然后,向应用上下文对象中添加一个应用监听器实现【即 ConditionEvaluationReportListener】,这里可查看 3.2 小节的内容。
  • 最后,如果 applicationContextGenericApplicationContext 的一个实例对象,则通过 ConditionEvaluationReport 的静态方法 get 来获取指定 Bean 工厂中的 条件评估报告 实例对象。

3.2 ConditionEvaluationReport 监听器

下面继续来查看 ConditionEvaluationReportListener 的源码:

在这里插入图片描述

阅读上述源码,可以看到 ConditionEvaluationReportListener 实现了 GenericApplicationListener 接口,继续翻看 GenericApplicationListener 接口源码:

在这里插入图片描述

继续翻看 SmartApplicationListener 接口源码:

在这里插入图片描述

从上述源码中,我们发现 GenericApplicationListener 继承了 SmartApplicationListener,而 SmartApplicationListener 则继承了 ApplicationListener<ApplicationEvent>

GenericApplicationListenerSpring 框架中的一个接口,它扩展了 ApplicationListener 接口,暴露了更多的元数据,如支持的事件和源类型。在 Spring Framework 4.2 及更高版本中,GenericApplicationListener 替代了基于类的 SmartApplicationListener,允许你使用 ResolvableType 来指定支持的事件类型,而不仅仅是 Class 类型,这样就可以在运行时更准确地解析和匹配事件类型。

知识点: ResolvableTypeSpring 框架中提供的一个工具类,它用于在运行时解析和处理 Java 泛型信息。在 Java 5 引入泛型之后,为了支持泛型,新增了 Type 类来表示 Java 中的某种类型。然而,反射包中提供的方法在获取泛型类型时,通常返回的是 Type 或其子类的实例,使用时可能需要进行繁琐的强制类型转换。ResolvableType 的出现就是为了简化对泛型信息的获取和处理。它能够将 ClassFieldMethod 等描述为 ResolvableType(即转换为 Type),从而方便地进行泛型的解析和操作。通过使用 ResolvableType,你可以轻松地获取 类、接口、属性、方法 等的泛型信息,而无需进行复杂的类型转换或编写繁琐的代码。

现在我们再来看看 ConditionEvaluationReportListener 中重写的 supportsEventType(ResolvableType) 方法:

在这里插入图片描述

也就是说,该监听器实际上监听是如下两个事件:

  • ContextRefreshedEvent :上下文刷新事件。该事件会在 ApplicationContext 完成初始化或刷新时发布。
  • ApplicationFailedEvent :应用启动失败事件。该事件是在 Spring Boot 应用启动失败时触发,一般发生在 ApplicationStartedEvent 事件之后。

我们继续查看 ConditionEvaluationReportListener 的核心方法 onApplicationEvent ,发现它直接调用了 ConditionEvaluationReportLoggingListener 中的 onApplicationEvent 方法,来实现条件评估报告的日志打印功能。

3.3 onApplicationEvent 方法

我们继续查看 ConditionEvaluationReportLoggingListener 中的 onApplicationEvent 方法:

在这里插入图片描述

从上图中,可以看到这里针对 3.2 中监听器监听的两个事件分别进行了处理,而这里的核心方法就是 logAutoConfigurationReport(boolean) 方法。

继续查看 logAutoConfigurationReport(boolean) 方法:

在这里插入图片描述

从上图中,我们可以简单总结一下:

  • 首先,如果条件评估报告 report 为空,则通过 ConditionEvaluationReport 的静态方法 get 来获取当前应用上下文指定的 Bean 工厂中的 条件评估报告 实例对象。
  • 判断 report 中的条件评估结果是否为空?
    • 如果不为空,判断条件评估报告的日志级别
      • 如果是 INFO 级别 ,则继续
        • 如果当前允许记录 INFO 级别日志,则按 INFO 级别输出相关的条件评估结果的日志信息。
      • 如果是 DEBUG 级别,则继续
        • 如果当前允许记录 DEBUG 级别日志,则按 DEBUG 级别输出相关的条件评估结果的日志信息。

3.4 条件评估报告的打印展示

首先,我们在当前 Spring Boot 项目中设置当前的日志级别为 DEBUG【当然还可以指定其他日志配置文件,这里不展开讲了】:

在这里插入图片描述

运行我们的自测类或者应用主类,可以看到如下的运行结果:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

从上述运行结果中,可以看出条件评估报告中包含如下的内容:

  • Positive matches正匹配,即 @Conditional 条件为真时,相关的配置类被Spring 容器加载,配置类中定义的 bean 和其他组件将被创建并添加到 Spring 的应用上下文中。
  • Negative matches负匹配,即 @Conditional 条件为假时,相关的配置类未被 Spring 容器加载。尽管相关的配置类存在于项目中,但由于某些条件不满足(如缺少必要的依赖或配置),Spring 容器不会创建该配置类中定义的 bean
  • Exclusions排除,即明确要排除的配置类,这些被排除的自动配置类中的组件将不会被创建。
  • Unconditional classes无条件类,即自动配置类不包含任何类级别的条件。与 Positive matchesNegative matches 不同,这些类不依赖于任何特定的条件来决定是否加载。它们总是会被 Spring 容器处理,无论其他条件如何。

四、总结

本篇 Huazie 带大家一起分析了 spring-boot-autoconfigure 子模块中预置的另一个应用上下文初始化器实现 ConditionEvaluationReportLoggingListener ,它实现了条件评估报告的打印记录功能,极大地方便了开发者定位配置类加载问题。

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

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

相关文章

【嵌入式智能产品开发实战】(十二)—— 政安晨:通过ARM-Linux掌握基本技能【运行环境】

目录 简述 开始 操作系统环境下的程序运行 裸机环境下的程序运行 程序入口main()函数分析 BSS段的小提示 政安晨的个人主页&#xff1a;政安晨 欢迎 &#x1f44d;点赞✍评论⭐收藏 收录专栏: 嵌入式智能产品开发实战 希望政安晨的博客能够对您有所裨益&#xff0c;如有不…

Python 后端 Flask 使用 Flask-SocketIO、前端 Vue3 实现长连接 Websocket 通信详细教程(更新中)

Flask 安装 Flask-Socketio Flask-SocketIO 第三方库使 Flask 应用程序可以实现客户端和服务器之间的低延迟双向通信。客户端应用程序可以使用 Javascript、Python、C、Java 和 Swift 中的任何 SocketIO 客户端库或任何其他兼容客户端来建立与服务器的永久连接。 Flask-Socke…

施耐德 PLC 控制系统 产品 + 软件总体介绍 2020

参考 2020.7 官方说明视频&#xff1a;https://www.bilibili.com/video/BV1Mi4y1G7Qc/ 总体说明 施耐德作为工业控制界巨头&#xff08;公认的几大巨头&#xff1a;西门子、AB、施耐德&#xff09;&#xff0c;PLC 控制器产品线很庞大&#xff0c;涵盖了高中低的完整产品线&…

代码随想录Day24:回溯算法Part1

回溯算法理论&#xff1a; Leetcode 77. 组合 这道题其实有点绕的我头晕&#xff0c;对于start index的解释我能够理解&#xff0c;但是我很难去想清楚他是如何在一次次递归中变化的因为他在for循环外面扮演我们每一次在一个数字找完了他开头的所有组合之后&#xff0c;就把st…

永磁同步电机PMSM和直流无刷电机BLDCM整理

刚完成的永磁同步电机的助力转向项目&#xff0c;接下来又遇到一个直流无刷电机的项目。刚好有时间将两个电机控制的异同和经典的控制方案总结一下。首先解释一下PMSW和BLDCM的含义。PMSW(Permanent Magnet Synchronous Motor)永磁同步电机的缩写&#xff1b;BLDCM(BrushLess D…

Datacom HCIP笔记-OSPF协议 之三

从骨干区域传来的三类LSA不再传回骨干区域 VLINK 1、只要创建的VLINK的路由器都是ABR 2、VLINK永远属于区域0的链路。 3、VLINK只能在非骨干区域创建&#xff0c;只能跨越一个非骨干区域。 4、特殊区域不能创建VLINK 5、用于修复不连续的骨干区域 6、将非骨干区域和骨干区域直接…

财务管理系统的设计与实现|Springboot+ Mysql+Java+ B/S结构(可运行源码+数据库+设计文档)

本项目包含可运行源码数据库LW&#xff0c;文末可获取本项目的所有资料。 推荐阅读100套最新项目持续更新中..... 2024年计算机毕业论文&#xff08;设计&#xff09;学生选题参考合集推荐收藏&#xff08;包含Springboot、jsp、ssmvue等技术项目合集&#xff09; 目录 1. …

GridLayoutManager 中的一些坑

前言 如果GridLayoutManager使用item的布局都是wrap_cotent 那么会在布局更改时会出现一些出人意料的情况。&#xff08;本文完全不具备可读性和说教性&#xff0c;仅为博主方便查找问题&#xff09; 布局item: <!--layout_item.xml--> <?xml version"1.0&qu…

Python爬虫:爬虫常用伪装手段

目录 前言 一、设置User-Agent 二、设置Referer 三、使用代理IP 四、限制请求频率 总结 前言 随着互联网的快速发展&#xff0c;爬虫技术在网络数据采集方面发挥着重要的作用。然而&#xff0c;由于爬虫的使用可能会对被爬取的网站造成一定的压力&#xff0c;因此&#…

政安晨:【Keras机器学习实践要点】(十二)—— 迁移学习和微调

目录 设置 介绍 冻结层&#xff1a;了解可训练属性 可训练属性的递归设置 典型的迁移学习工作流程 微调 关于compile()和trainable的重要说明 BatchNormalization层的重要注意事项 政安晨的个人主页&#xff1a;政安晨 欢迎 &#x1f44d;点赞✍评论⭐收藏 收录专栏: T…

基于Uni-app的体育场馆预约系统的设计与实现

文章目录 基于Uni-app的体育场馆预约系统的设计与实现1、前言介绍2、开发技术简介3、系统功能图3、功能实现4、库表设计5、关键代码6、源码获取7、 &#x1f389;写在最后 基于Uni-app的体育场馆预约系统的设计与实现 1、前言介绍 伴随着信息技术与互联网技术的不断发展&#…

轻量应用服务器16核32G28M腾讯云租用优惠价格4224元15个月

腾讯云16核32G服务器租用价格4224元15个月&#xff0c;买一年送3个月&#xff0c;配置为&#xff1a;轻量16核32G28M、380GB SSD盘、6000GB月流量、28M带宽&#xff0c;腾讯云优惠活动 yunfuwuqiba.com/go/txy 活动链接打开如下图&#xff1a; 腾讯云16核32G服务器租用价格 腾讯…

Nginx的反向代理

Nginx的反向代理 location ^~ /aaa {proxy_pass http://192.168.15.78/; } 1. 跨域 2.Nginx 代理服务器缓存 3.Nginx 负载均衡 4. 动静分离 Nginx的跨域 跨源资源共享 (CORS) 是一种机制&#xff0c;它使用额外的 HTTP 标头让用户代理获得访问来自不同来域的服务器上选定资…

怎么快速上手虚拟化(容器)技术——以 Docker 为例

Docker 整体介绍 Docker 是一种使用 Go 语言开发的容器工具。所谓容器&#xff0c;实际上是一种虚拟化技术&#xff0c;用于为应用提供虚拟化的运行环境&#xff0c;相较于虚拟机具有轻量级、低延迟的特性。 下面是对上述介绍的说明&#xff1a; 应用程序运行需要一定的依赖…

qtcreator的信号槽链接

在ui文件中简单创建一个信号槽连接并保存可以在ui_mainwindow.h下 class Ui_MainWindow 类 void setupUi(QMainWindow *MainWindow)函数 找到对应代码 QObject::connect(pushButton, SIGNAL(clicked()), MainWindow, SLOT(close())); 下拉&#xff0c;由于 class MainWind…

@Transactional使用细节

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl 动态代理回顾 Spring的声明式事务管理是建立在 AOP 的基础之上的。Spring AOP是通过动态代理实现的。如果代理对象实现了接口&#xff0c;则使用JDK的动态代理&#xff1b;…

SpringBoot整合knife4J 3.0.3

Knife4j的前身是swagger-bootstrap-ui,前身swagger-bootstrap-ui是一个纯swagger-ui的ui皮肤项目。项目正式更名为knife4j,取名knife4j是希望她能像一把匕首一样小巧,轻量,并且功能强悍,更名也是希望把她做成一个为Swagger接口文档服务的通用性解决方案,不仅仅只是专注于前端Ui…

【IP组播】PIM-SM的RP、RPF校验

目录 一&#xff1a;PIM-SM的RP 原理概述 实验目的 实验内容 实验拓扑 1.基本配置 2.配置IGP 3.配置PIM-SM和静态RP 4.配置动态RP 5.配置Anycast RP 二&#xff1a; RPF校验 原理概述 实验目的 实验内容 实验拓扑 1.基本配置 2.配置IGP 3.配置PIM-DM 4.RPF校…

LeetCode_876(链表的中间结点)

//双指针//时间复杂度O(n) 空间复杂度O(1)public ListNode middleNode(ListNode head) {ListNode slowhead,fast head;while (fast!null && fast.next!null){slow slow.next;fast fast.next.next;}return slow;} 1->2->3->4->5->null 快指针移动两个…

如何创建一个TCP多人聊天室?

一、什么是TCP&#xff1f; TCP&#xff08;Transmission Control Protocol&#xff09;是一种可靠的 面向连接的协议 &#xff0c;可以保证数据在传输过程中不会丢失、重复或乱序。 利用TCP实现简单聊天程序&#xff0c;需要客户端和服务器端之间建立TCP连接&#xff0c;并通…