Spring Boot - Application Events 的发布顺序_ApplicationEnvironmentPreparedEvent

文章目录

  • Pre
  • 概述
  • Code
  • 源码分析

在这里插入图片描述


Pre

Spring Boot - Application Events 的发布顺序_ApplicationEnvironmentPreparedEvent


概述

Spring Boot 的广播机制是基于观察者模式实现的,它允许在 Spring 应用程序中发布和监听事件。这种机制的主要目的是为了实现解耦,使得应用程序中的不同组件可以独立地改变和复用逻辑,而无需直接进行通信。

在 Spring Boot 中,事件发布和监听的机制是通过 ApplicationEventApplicationListener 以及事件发布者(ApplicationEventPublisher)来实现的。其中,ApplicationEvent 是所有自定义事件的基础,自定义事件需要继承自它。

ApplicationListener 是监听特定事件并做出响应的接口,开发者可以通过实现该接口来定义自己的监听器。事件发布者(通常由 Spring 的 ApplicationContext 担任)负责发布事件。


ApplicationEnvironmentPreparedEvent事件在Spring Boot应用程序中非常有用。当应用程序环境准备就绪时,可以使用此事件来执行一些初始化操作,例如设置系统属性、加载配置文件、动态修改环境等。

通过监听ApplicationEnvironmentPreparedEvent事件,我们可以在Spring Boot应用程序启动之前对环境进行一些自定义的配置和修改,以满足特定的需求。例如,可以在此事件中动态加载不同的配置文件,根据环境变量设置不同的系统属性,或者执行其他与环境相关的初始化操作


Code

package com.artisan.event;import org.springframework.boot.SpringApplication;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertySource;import java.util.HashMap;
import java.util.Map;
import java.util.Properties;/*** @author 小工匠* @version 1.0* @mark: show me the code , change the world*/
public class ApplicationEnvironmentPreparedListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {/*** ApplicationEnvironmentPreparedEvent 在应用程序环境准备时触发,此时应用程序上下文尚未初始化。* <p>* 通过侦听此事件,我们可以访问环境的属性、配置文件和配置源。* 然后,我们可以执行一些任务,例如修改属性值、添加或删除配置源、激活特定配置文件或根据环境状态应用自定义逻辑。* <p>* <p>* 为了处理事件 ApplicationEnvironmentPreparedEvent ,我们可以通过实现 ApplicationListener ApplicationEnvironmentPreparedEvent 作为泛型类型的接口来创建自定义事件侦听器。* 此侦听器可以在主应用程序类中手动注册** @param event the event to respond to*/@Overridepublic void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {System.out.println("--------------------> Handling ApplicationEnvironmentPreparedEvent here!");ConfigurableEnvironment environment = event.getEnvironment();// 添加自定义属性创建一个Map对象Map<String, Object> propertiesMap = new HashMap<>();propertiesMap.put("myDynamicProperty", "myValue");// 将Map转换为PropertiesProperties properties = new Properties();properties.putAll(propertiesMap);// 创建一个PropertiesPropertySourcePropertySource<?> propertySource = new PropertiesPropertySource("dynamicProperties", properties);// 将PropertiesPropertySource添加到环境属性源中environment.getPropertySources().addFirst(propertySource);SpringApplication springApplication = event.getSpringApplication();springApplication.setDefaultProperties(properties);}
}

如何使用呢?

方式一:

@SpringBootApplication
public class LifeCycleApplication {/*** 除了手工add , 在 META-INF下面 的 spring.factories 里增加* org.springframework.context.ApplicationListener=自定义的listener 也可以** @param args*/public static void main(String[] args) {SpringApplication springApplication = new SpringApplication(LifeCycleApplication.class);// 当我们运行 Spring Boot 应用程序时,将调用 的方法 ApplicationEnvironmentPreparedListener#onApplicationEvent() 允许我们根据需要访问和修改应用程序的环境springApplication.addListeners(new ApplicationEnvironmentPreparedListener());springApplication.run(args);}}

方式二: 通过spring.factories 配置

在这里插入图片描述

org.springframework.context.ApplicationListener=\
com.artisan.event.ApplicationEnvironmentPreparedListener

运行日志
在这里插入图片描述


源码分析

首先main方法启动入口

SpringApplication.run(LifeCycleApplication.class, args);

跟进去

public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {return run(new Class<?>[] { primarySource }, args);}

继续

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {return new SpringApplication(primarySources).run(args);}

这里首先关注 new SpringApplication(primarySources)

new SpringApplication(primarySources)

	/*** Create a new {@link SpringApplication} instance. The application context will load* beans from the specified primary sources (see {@link SpringApplication class-level}* documentation for details. The instance can be customized before calling* {@link #run(String...)}.* @param resourceLoader the resource loader to use* @param primarySources the primary bean sources* @see #run(Class, String[])* @see #setSources(Set)*/@SuppressWarnings({ "unchecked", "rawtypes" })public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {this.resourceLoader = resourceLoader;Assert.notNull(primarySources, "PrimarySources must not be null");this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));this.webApplicationType = WebApplicationType.deduceFromClasspath();this.bootstrappers = new ArrayList<>(getSpringFactoriesInstances(Bootstrapper.class));setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));this.mainApplicationClass = deduceMainApplicationClass();}

聚焦 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));


run

继续run

// 开始启动Spring应用程序
public ConfigurableApplicationContext run(String... args) {StopWatch stopWatch = new StopWatch(); // 创建一个计时器stopWatch.start(); // 开始计时DefaultBootstrapContext bootstrapContext = createBootstrapContext(); // 创建引导上下文ConfigurableApplicationContext context = null; // Spring应用上下文,初始化为nullconfigureHeadlessProperty(); // 配置无头属性(如:是否在浏览器中运行)SpringApplicationRunListeners listeners = getRunListeners(args); // 获取运行监听器listeners.starting(bootstrapContext, this.mainApplicationClass); // 通知监听器启动过程开始try {ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); // 创建应用参数ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments); // 预备环境configureIgnoreBeanInfo(environment); // 配置忽略BeanInfoBanner printedBanner = printBanner(environment); // 打印Bannercontext = createApplicationContext(); // 创建应用上下文context.setApplicationStartup(this.applicationStartup); // 设置应用启动状态prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner); // 准备上下文refreshContext(context); // 刷新上下文,执行Bean的生命周期afterRefresh(context, applicationArguments); // 刷新后的操作stopWatch.stop(); // 停止计时if (this.logStartupInfo) { // 如果需要记录启动信息new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); // 记录启动信息}listeners.started(context); // 通知监听器启动完成callRunners(context, applicationArguments); // 调用Runner}catch (Throwable ex) {handleRunFailure(context, ex, listeners); // 处理运行失败throw new IllegalStateException(ex); // 抛出异常}try {listeners.running(context); // 通知监听器运行中}catch (Throwable ex) {handleRunFailure(context, ex, null); // 处理运行失败throw new IllegalStateException(ex); // 抛出异常}return context; // 返回应用上下文
}

我们重点看

  ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);

继续

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {// 获取或创建环境对象ConfigurableEnvironment environment = getOrCreateEnvironment();// 使用应用程序参数配置环境configureEnvironment(environment, applicationArguments.getSourceArgs());// 将配置属性源附加到环境中ConfigurationPropertySources.attach(environment);// 通知监听器环境已经准备好listeners.environmentPrepared(bootstrapContext, environment);// 将默认属性属性源移动到环境的末尾DefaultPropertiesPropertySource.moveToEnd(environment);// 配置额外的配置文件configureAdditionalProfiles(environment);// 将环境绑定到Spring应用程序bindToSpringApplication(environment);// 如果不是自定义环境,则转换环境if (!this.isCustomEnvironment) {environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,deduceEnvironmentClass());}// 再次将配置属性源附加到环境,确保所有属性源都被正确加载ConfigurationPropertySources.attach(environment);// 返回配置好的环境对象return environment;
}

重点关注: listeners.environmentPrepared(bootstrapContext, environment);

在这里插入图片描述

 void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {doWithListeners("spring.boot.application.environment-prepared",(listener) -> listener.environmentPrepared(bootstrapContext, environment));}

继续 listener.environmentPrepared(bootstrapContext, environment)

发布事件

	@Overridepublic void environmentPrepared(ConfigurableBootstrapContext bootstrapContext,ConfigurableEnvironment environment) {this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(bootstrapContext, this.application, this.args, environment));}

继续

	@Overridepublic void multicastEvent(ApplicationEvent event) {multicastEvent(event, resolveDefaultEventType(event));}
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {// 如果eventType不为null,则直接使用它;否则,使用resolveDefaultEventType方法来解析事件的默认类型。ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));// 获取一个线程池执行器,它用于异步执行监听器调用。Executor executor = getTaskExecutor();// 获取所有对应该事件类型的监听器。for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {// 如果执行器不为null,则使用它来异步执行监听器调用;// 否则,直接同步调用监听器。if (executor != null) {executor.execute(() -> invokeListener(listener, event));}else {invokeListener(listener, event);}}
}

继续

private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {try {listener.onApplicationEvent(event);}catch (ClassCastException ex) {......}}

就到了我们自定义实现的代码逻辑中了。

     @Overridepublic void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {System.out.println("--------------------> Handling ApplicationEnvironmentPreparedEvent here!");..........}

在这里插入图片描述

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

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

相关文章

RabbitMQ入门到实战——高级篇

消息的可靠性 生产者的可靠性&#xff08;确保消息一定到达MQ&#xff09; 生产者重连 这⾥除了enabled是false外&#xff0c;其他 initial-interval 等默认都是⼀样的值。 生产者确认 生产者确认代码实现 application中增加配置&#xff1a;&#xff08;publisher-returns…

《MySQL系列-InnoDB引擎06》MySQL锁介绍

文章目录 第六章 锁1 什么是锁2 lock与latch3 InnoDB存储引擎中的锁3.1 锁的类型3.2 一致性非锁定读3.3 一致性锁定读3.4 自增长与锁3.5 外键和锁 4 锁的算法4.1 行锁的三种算法4.2 解决Phantom Problem 5 锁问题5.1 脏读5.2 不可重复读5.3 丢失更新 6 阻塞7 死锁 第六章 锁 开…

深度解析Cron表达式:精确控制任务调度的艺术

深度解析Cron表达式&#xff1a;精确控制任务调度的艺术 希望我们都可以满怀期待的路过每一个转角 去遇见 那个属于自己故事的开始 去追寻那个最真实的自己 去放下 去拿起 安然&#xff0c;自得&#xff0c;不受世俗牵绊… 导言 在计算机科学领域&#xff0c;任务调度是一项关…

【PyQt5设计】:自动点击神器 - 解决重复性的点击和输入操作

文章目录 自动点击神器介绍测试窗口介绍自动点击神器的使用教程资源领取注意事项 自动点击神器介绍 本次使用PyQt5设计的【自动点击神器】旨在解决重复性的点击工作&#xff0c;解放双手&#xff0c;具有及时性和准确性&#xff0c;可选择坐标位置或图片两种方式实现鼠标的定位…

横版动作闯关游戏:幽灵之歌 GHOST SONG 中文版

在洛里安荒凉的卫星上&#xff0c;一件长期休眠的死亡服从沉睡中醒来。踏上发现自我、古老谜团和宇宙骇物的氛围2D冒险之旅。探索蜿蜒的洞穴&#xff0c;获得新的能力来揭开这个外星世界埋藏已久的秘密。 游戏特点 发现地下之物 探索这个广阔而美丽如画&#xff0c;充满密室和诡…

一个常用的项目架构图

给大家分享一个常用的架构图&#xff0c;需要使用的可以免费那走&#xff1a; 用户通过公网IP或者域名访问&#xff0c;穿过防火墙后&#xff0c;映射到nginx组件&#xff0c;在反向代理到各个模块&#xff1b;资源放在Nas盘&#xff0c;数据放在各个中间件&#xff1b;各个模块…

Android基于Matrix绘制PaintDrawable设置BitmapShader,以手指触点为中心显示原图像圆图,Kotlin

Android基于Matrix绘制PaintDrawable设置BitmapShader&#xff0c;以手指触点为中心显示原图像圆图&#xff0c;Kotlin 手指在上面的图上移动&#xff0c;“剪切”出上面图中以手指触点为中心的图&#xff08;半径图&#xff09;&#xff0c;然后在下面的ImageView显示。 impor…

中国大学生计算机设计大赛—人工智能实践赛赛道—赛后感想

1.比赛介绍 中国大学生计算机设计大赛是我国高校面向本科生最早的赛事之一&#xff0c;是全国普通高校大学生竞赛排行榜榜单赛事之一。自2008年开赛至2019年&#xff0c;一直由教育部高校与计算机相关教指委等或独立或联合主办。大赛的目的是以赛促学、以赛促教、以赛促创&…

【技能拾遗】——如何寻找/制作电子书

&#x1f4d6; 前言&#xff1a;无纸化学习已经越来越流行了&#xff0c;尤其是这几年大家上网课&#xff0c;把厚厚的课本和笔记装进pad里面&#xff0c;其便利性想必大家都深有感受。但是还有不少同学不知道该如何去找正规教材的PDF版本&#xff0c;下面就以我这两年的无纸化…

RTL编码(1)——概述

一、RTL级描述 RTL&#xff08;Register Transfer Level&#xff09;级&#xff1a;寄存器&#xff0b;组合逻辑&#xff0c;其功能与时序用Verilog HDL&#xff08;以下简称Verilog&#xff09;或VHDL代码描述。 RTL描述包含了同步数字电路最重要的三个特征&#xff1a;组合逻…

外贸自建站新手教程指南?海洋建站的技巧?

外贸自建站怎么做比较好&#xff1f;搭建外贸网站的参数有哪些&#xff1f; 外贸自建站成为许多企业迈向国际市场的重要一步。随着全球经济的不断发展&#xff0c;搭建一个专业而有效的外贸自建站对企业而言至关重要。海洋建站将为您提供一份详尽的新手教程指南&#xff0c;帮…

通过Studio 3T对Mongodb进行 创建数据库/集合 增删查改集合文档操作

首先 你需要安装Studio 3T 以及启动 Mongodb服务 具体可以参考我的文章 Studio 3T客户端连接Mongodb数据库服务 我们之前 通过 use 数据库名随便输切换是可以的 但除了这里能看到的 它们都仅存在于内存 我们右键顶部菜单 选择 添加数据库/创建数据库 这里 我们输入数据库名称…

HarmonyOS自定义组件生命周期函数介绍

aboutToAppear 在创建自定义组件的新实例后&#xff0c;在执行其build()函数之前执行。允许在aboutToAppear函数中改变状态变量&#xff0c;更改将在后续执行build()函数中生效。 aboutToDisappear 在自定义组件析构销毁之前执行。不允许在aboutToDisappear函数中改变状态变…

java基础day04 -- 命令行运行java文件

package com.exmaple;/*** 命令行参数*/ public class ArgsOfMain {public static void main(String[] args) {//增强for循环for(String arg : args){System.out.println(arg);}} }当我打开idea终端运行javac命令完成后&#xff08;需要配置java环境变量&#xff0c;注意idea使…

安德尔房产数据分析

安德尔房产数据分析 安德尔房产数据分析介绍挑战概要数据集添加地理数据数据清理数据分析与解释结论困难与解决方案 安德尔房产数据分析 介绍 在这个挑战中&#xff0c;使用了 pandas 和数据可视化库&#xff08;Matplotlib、Seaborn&#xff09;来对数据集进行分析&#xff…

MySQL中order by是怎么工作的?

在如上图中所示的explain的执行结果中&#xff0c;Extra字段中的“Using filesort”表示的就是需要排序&#xff0c;MySQL会给每个线程分配一块内存用于排序&#xff0c;称为sort_buffer。 索引city如上图所示 上述语句的执行流程如下&#xff1a; 1、初始化sort_buffer&…

每天刷两道题——第十二天+第十三天

1.1合并区间 以数组 i n t e r v a l s intervals intervals 表示若干个区间的集合&#xff0c;其中单个区间为 i n t e r v a l s [ i ] [ s t a r t i , e n d i ] intervals[i] [starti, endi] intervals[i][starti,endi] 。请你合并所有重叠的区间&#xff0c;并返回 …

C语言实现简易n子棋小游戏(代码含注解)

利用C语言简单实现一个n子棋小游戏&#xff0c;棋盘大小由自己定义 将源文件分为 执行游戏的测试文件(test.c)和保存游戏运行逻辑的相关函数的文件(game.c) 头文件中声明符号和函数的定义(game.h) 游戏执行主要依靠二维数组实现&#xff0c;电脑走棋采用随机值的方法简易地…

【OpenCV学习笔记05】- 鼠标作为画笔

这是对于 OpenCV 官方文档的 GUI 功能的学习笔记。学习笔记中会记录官方给出的例子&#xff0c;也会给出自己根据官方的例子完成的更改代码&#xff0c;同样彩蛋的实现也会结合多个知识点一起实现一些小功能&#xff0c;来帮助我们对学会的知识点进行结合应用。 如果有喜欢我笔…

用win系统搭建Minecraft世界服务器,MC开服教程,小白开服教程

雨云VPS用Windows系统搭建我的世界世界服务器&#xff0c;Minecraft开服教程&#xff0c;小白开服教程&#xff0c;MC 1.19.4版本服务器搭建教程。 此教程使用 Mohist 1.19.4 服务端&#xff0c;此服务端支持Forge模组和Bukkit/Spigot/Paper插件&#xff0c;如果需要开其他服务…