Spring Boot - Application Events 的发布顺序_ApplicationFailedEvent

文章目录

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

在这里插入图片描述


Pre

Spring Boot - Application Events 的发布顺序_ApplicationEnvironmentPreparedEvent


概述

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

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

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


在Spring框架中,ApplicationFailedEvent 是一个特殊的事件,它代表了应用程序在启动过程中遇到的失败情况。这个事件是在Spring的应用程序生命周期中,当应用程序启动失败时触发的。

ApplicationFailedEvent 事件通常包含了有关失败原因的信息,例如异常类型、异常消息、发生错误的类和方法、以及失败发生的时间等。这个事件是Spring事件机制的一部分,它允许开发者在应用程序中实现事件驱动的设计。

在Spring框架中,事件机制是基于观察者模式的实现。事件发布者和事件监听器通过事件进行通信。在Spring中,事件发布者通常是通过 ApplicationEventPublisher 接口来进行操作的,而事件监听器则通过实现 ApplicationListener 接口来定义。

当Spring应用程序启动时,它会经历多个阶段。如果在某个阶段发生了错误,比如在初始化数据源时出现了异常,Spring会发布 ApplicationFailedEvent 事件。事件监听器可以监听这个事件,并对事件进行处理,比如记录日志、发送警报或者进行补偿操作等。

在Spring Boot应用程序中,ApplicationFailedEvent 事件也可以被用来处理启动时的异常情况。Spring Boot提供了一种更简化的方式来监听这个事件,即使用 @EventListener 注解。这种方式可以让开发者更容易地编写事件监听器,而不需要实现复杂的接口。

例如,以下是一个简单的 @EventListener 注解的使用示例,用于监听 ApplicationFailedEvent 事件:

@Component
public class ApplicationFailedListener {@EventListenerpublic void onApplicationFailedEvent(ApplicationFailedEvent event) {Throwable throwable = event.getException();// 对异常进行处理,比如记录日志System.err.println("Application failed to start: " + throwable.getMessage());}
}

当应用程序启动失败时,这个监听器会被触发,并可以执行相应的错误处理逻辑。这样,开发者可以更好地管理应用程序的启动过程,并在遇到失败时进行适当的响应。


Code

package com.artisan.event;import org.springframework.boot.context.event.ApplicationFailedEvent;
import org.springframework.context.ApplicationListener;/*** @author 小工匠* @version 1.0* @mark: show me the code , change the world*/
public class ApplicationFailedListener implements ApplicationListener<ApplicationFailedEvent> {@Overridepublic void onApplicationEvent(ApplicationFailedEvent event) {System.out.println("--------------------> Handling ApplicationFailedEvent here!");Throwable throwable = event.getException();// 对异常进行处理,比如记录日志System.err.println("Application failed to start: " + throwable.getMessage());}
}

如何使用呢?

方式一:

@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);springApplication.addListeners(new ApplicationFailedListener());springApplication.run(args);}}

方式二: 通过spring.factories 配置

在这里插入图片描述

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

运行日志

在这里插入图片描述


源码分析

首先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; // 返回应用上下文
}

我们重点看

  handleRunFailure(context, ex, listeners); // 处理运行失败

继续

private void handleRunFailure(ConfigurableApplicationContext context, Throwable exception,SpringApplicationRunListeners listeners) {try {try {handleExitCode(context, exception);if (listeners != null) {listeners.failed(context, exception);}}finally {reportFailure(getExceptionReporters(context), exception);if (context != null) {context.close();}}}catch (Exception ex) {logger.warn("Unable to close ApplicationContext", ex);}ReflectionUtils.rethrowRuntimeException(exception);}

继续 listeners.failed(context, exception);

	void failed(ConfigurableApplicationContext context, Throwable exception) {doWithListeners("spring.boot.application.failed",(listener) -> callFailedListener(listener, context, exception), (step) -> {step.tag("exception", exception.getClass().toString());step.tag("message", exception.getMessage());});}

继续 callFailedListener;

private void callFailedListener(SpringApplicationRunListener listener, ConfigurableApplicationContext context,Throwable exception) {try {listener.failed(context, exception);}catch (Throwable ex) {if (exception == null) {ReflectionUtils.rethrowRuntimeException(ex);}if (this.log.isDebugEnabled()) {this.log.error("Error handling failed", ex);}else {String message = ex.getMessage();message = (message != null) ? message : "no error message";this.log.warn("Error handling failed (" + message + ")");}}}
@Overridepublic void failed(ConfigurableApplicationContext context, Throwable exception) {ApplicationFailedEvent event = new ApplicationFailedEvent(this.application, this.args, context, exception);if (context != null && context.isActive()) {// Listeners have been registered to the application context so we should// use it at this point if we cancontext.publishEvent(event);}else {// An inactive context may not have a multicaster so we use our multicaster to// call all of the context's listeners insteadif (context instanceof AbstractApplicationContext) {for (ApplicationListener<?> listener : ((AbstractApplicationContext) context).getApplicationListeners()) {this.initialMulticaster.addApplicationListener(listener);}}this.initialMulticaster.setErrorHandler(new LoggingErrorHandler());this.initialMulticaster.multicastEvent(event);}}

context.publishEvent(event);

@Overridepublic void failed(ConfigurableApplicationContext context, Throwable exception) {ApplicationFailedEvent event = new ApplicationFailedEvent(this.application, this.args, context, exception);if (context != null && context.isActive()) {// Listeners have been registered to the application context so we should// use it at this point if we cancontext.publishEvent(event);}else {// An inactive context may not have a multicaster so we use our multicaster to// call all of the context's listeners insteadif (context instanceof AbstractApplicationContext) {for (ApplicationListener<?> listener : ((AbstractApplicationContext) context).getApplicationListeners()) {this.initialMulticaster.addApplicationListener(listener);}}this.initialMulticaster.setErrorHandler(new LoggingErrorHandler());this.initialMulticaster.multicastEvent(event);}}

继续this.initialMulticaster.multicastEvent(event);

	@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);}}
}

继续

/*** 调用一个事件监听器的方法。** @param listener 要调用的监听器* @param event 要处理的事件*/
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {try {// 直接调用监听器的onApplicationEvent方法listener.onApplicationEvent(event);}catch (ClassCastException ex) {String msg = ex.getMessage();// 如果消息为null或者消息匹配事件类的预期类型,则忽略异常并记录debug日志if (msg == null || matchesClassCastMessage(msg, event.getClass())) {Log logger = LogFactory.getLog(getClass());if (logger.isTraceEnabled()) {logger.trace("Non-matching event type for listener: " + listener, ex);}}// 否则,抛出异常else {throw ex;}}
}

继续 就会调到我们自己的业务逻辑了

 @Overridepublic void onApplicationEvent(ApplicationFailedEvent event) {System.out.println("--------------------> Handling ApplicationFailedEvent here!");Throwable throwable = event.getException();// 对异常进行处理,比如记录日志System.err.println("Application failed to start: " + throwable.getMessage());}

在这里插入图片描述

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

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

相关文章

选择安全数据交换系统时 要考虑哪些因素?

安全数据交换系统是一种专门设计用于在不同的网络环境&#xff08;如内部不同网络&#xff0c;内部网络和外部网络&#xff09;之间安全传输数据的解决方案。它通常包括一系列的技术和流程&#xff0c;旨在确保数据在传输过程中的完整性、机密性和可用性。 安全数据交换系统可以…

Maven工程 — 继承与聚合 相关知识点详解

简介&#xff1a;这篇帖子主要讲解Maven工程中的继承与聚合的相关知识点&#xff0c;用简洁的语言和小编自己的理解&#xff0c;深入浅出的说明Maven工程的继承与聚合。 目录 1、继承 1.1 继承关系的实现 1.2 版本锁定 2、聚合 2.1 聚合方法 3、总结 1、继承 图 1-1 继承…

springBoot项目打包发布

打包 项目代码编写完成后&#xff0c;在pom.xml文件中引用打包的插件&#xff1a; <!-- 打包插件坐标--><build><plugins><!--打包插件--><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-mave…

vue 指定区域可拖拽的限定拖拽区域的div(如仅弹窗标题可拖拽的弹窗)

<template><div class"container" ref"container"><div class"drag-box" v-drag><div class"win_head">弹窗标题</div><div class"win_content">弹窗内容</div></div><…

探索设计模式的魅力:抽象工厂模式的艺术

抽象工厂模式&#xff08;Abstract Factory Pattern&#xff09;是一种创建型设计模式&#xff0c;用于在不指定具体类的情况下创建一系列相关或相互依赖的对象。它提供了一个接口&#xff0c;用于创建一系列“家族”或相关依赖对象&#xff0c;而无需指定它们的具体类。 主要参…

Jest 28发布

Jest 28终于来了&#xff0c;它带来了一些长期以来一直要求的特性&#xff0c;比如支持跨多台机器的测试运行分片、包导出和自定义假计时器行为的能力。 新特性 安装大小减少了大约 1/3 正如在去年的Jest 27博客中所宣布的那样&#xff0c;已经从默认安装中删除了一些不再默…

阿赵UE学习笔记——11、地形系统

阿赵UE学习笔记目录 大家好&#xff0c;我是阿赵。   继续学习虚幻引擎的用法&#xff0c;这次来学习一下虚幻引擎的地形系统的用法。 一、创建地形 在选项模式里面&#xff0c;选择地形&#xff1a; 进入到地形界面之后&#xff0c;需要先创建一个地形&#xff1a; 留意看…

stm32 - GPIO

stm32 - GPIO 基本结构输入输出 基本结构 所有GPIO都挂在APB2总线上 寄存器&#xff1a;内核通过APB2总线对寄存器进行读写&#xff0c;实现电平的读写 GPIO引脚的每一位对应寄存器中的某一位 GPIO中的驱动器是增加信号驱动能力的&#xff0c;用于增大驱动能力 输入 读取端口的…

吃瓜教程Task1:概览西瓜书+南瓜书第1、2章

由于本人之前已经学习过西瓜书&#xff0c;本次学习主要是对以往知识的查漏补缺&#xff0c;因此本博客记录了在学习西瓜书中容易混淆的点以及学习过程中的难点。更多学习内容可以参考下面的链接&#xff1a; 南瓜书的地址&#xff1a;https://github.com/datawhalechina/pumpk…

mobi文件怎么转换成pdf?

mobi文件怎么转换成pdf&#xff1f;在数字化时代&#xff0c;电子书籍成为了越来越受欢迎的阅读方式。我们可以通过多种格式的电子书来获取知识和娱乐&#xff0c;其中一种常见的格式就是Mobi文件。Mobi文件是亚马逊公司开发的一种电子书格式&#xff0c;它主要用于Kindle设备和…

C语言通过MSXML6.0读写XML文件(同时支持char[]和wchar_t[]字符数组)

开发环境&#xff1a;Visual Studio 2010 运行环境&#xff1a;Windows XP SP3 第一节 读取XML文件&#xff08;使用wchar_t[]字符数组&#xff09; /* 这个程序只能在C编译器下编译成功, 请确保源文件的扩展名为c */ #define COBJMACROS #include <stdio.h> #include …

Springboot 子工程构建完后无法找到springboot依赖

问题: 构建完子工程后无法找到SpringBootTest 解决方案: 最好用这个构建 https://www.cnblogs.com/he-wen/p/16735239.html 1.先观察项目目录 是否正确 2.观察子工程目录 3.看pom.xml中是否引用springboot依赖 4.检查代码 查看父项目是否包含子模块 查看子模块的父项目是否…

uniapp写微信小程序实现电子签名

写电子签名一定要注意的是一切全部按照手机上的适配来&#xff0c;为啥这么说呢&#xff0c;因为你在微信开发者工具中调试的时候认为是好的&#xff0c;正常的非常nice,当你发布版本的时候你会发现问题出来了。我下边的写法你可以直接用很简单。就是要记住canvas的几个属性和用…

基于SSM的校园闲置物品交易平台设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

教师语言的重要性体现在哪些方面?

教师&#xff0c;这个职业被誉为人类灵魂的工程师&#xff0c;他们的话语有着无可比拟的力量。有时&#xff0c;一句话就能点亮一个孩子的世界&#xff0c;也可以打破一个孩子的求知欲望。那么&#xff0c;教师话语的重要性究竟体现在哪些方面呢&#xff1f; 教师的话语是激发…

帮管家 CRM init 信息泄露漏洞

此文所提供的信息只为网络安全人员对自己所负责的网站、服务器等&#xff08;包括但不限于&#xff09;进行检测或维护参考&#xff0c;未经授权请勿利用文章中的技术资料对任何计算机系统进行入侵操作。利用此文所提供的信息而造成的直接或间接后果和损失&#xff0c;均由使用…

npm换源

检查现在的源地址 npm config get registry 使用淘宝镜像 npm config set registry https://registry.npm.taobao.org 使用官方镜像 npm config set registry https://registry.npmjs.org/

《WebKit 技术内幕》之三(2): WebKit 架构和模块

2.基于 Blink 的 Chrominum 浏览器结构 2.1 Chrominum 浏览器的架构及模块 Chromium也是基于WebKit&#xff08;Blink&#xff09;开发的&#xff0c;并且在WebKit的移植部分中&#xff0c;Chromium也做了很多有趣的事&#xff0c;所以通过Chromium可以了解如何基于WebKit构建浏…

大数据深度学习ResNet深度残差网络详解:网络结构解读与PyTorch实现教程

文章目录 大数据深度学习ResNet深度残差网络详解&#xff1a;网络结构解读与PyTorch实现教程一、深度残差网络&#xff08;Deep Residual Networks&#xff09;简介深度学习与网络深度的挑战残差学习的提出为什么ResNet有效&#xff1f; 二、深度学习与梯度消失问题梯度消失问题…

高斯Hack算法

背景 刷leetcode时&#xff0c;碰到一题需要求解n个bit中选择m个bit的所有组合集&#xff0c;我只想到了递归求解&#xff0c;没啥问题&#xff0c;但是在官方题解中看到了牛逼的方法(Gospers Hack)&#xff0c;故记录一下。 4bit中2个1的情况 0011b0101b0110b1001b1010b1100b…