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 {
    @EventListener
    public 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> {


    @Override
    public 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应用上下文,初始化为null
    
    configureHeadlessProperty(); // 配置无头属性(如:是否在浏览器中运行)

    SpringApplicationRunListeners listeners = getRunListeners(args); // 获取运行监听器
    listeners.starting(bootstrapContext, this.mainApplicationClass); // 通知监听器启动过程开始
    
    try {
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); // 创建应用参数
        ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments); // 预备环境
        configureIgnoreBeanInfo(environment); // 配置忽略BeanInfo
        
        Banner printedBanner = printBanner(environment); // 打印Banner
        context = 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 + ")");
			}
		}
	}
@Override
	public 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 can
			context.publishEvent(event);
		}
		else {
			// An inactive context may not have a multicaster so we use our multicaster to
			// call all of the context's listeners instead
			if (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);

@Override
	public 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 can
			context.publishEvent(event);
		}
		else {
			// An inactive context may not have a multicaster so we use our multicaster to
			// call all of the context's listeners instead
			if (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);

	@Override
	public 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;
        }
    }
}

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

 @Override
    public void onApplicationEvent(ApplicationFailedEvent event) {
        System.out.println("--------------------> Handling ApplicationFailedEvent here!");

        Throwable throwable = event.getException();
        // 对异常进行处理,比如记录日志
        System.err.println("Application failed to start: " + throwable.getMessage());
    }

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MySQL进阶45讲【1】基础架构:一条SQL查询语句是如何执行的?

1 前言 我们经常说&#xff0c;看一个事儿千万不要直接陷入细节里&#xff0c;应该先鸟瞰其全貌&#xff0c;这样能够帮助你从高维度理解问题。同样&#xff0c;对于MySQL的学习也是这样。平时我们使用数据库&#xff0c;看到的通常都是一个整体。比如&#xff0c;有个最简单的…

浅谈敏捷开发的思维

什么是敏捷 Agile&#xff08;敏捷&#xff09;来源于敏捷宣言&#xff0c;宣言明确指出&#xff0c;“敏捷”&#xff1a; 不是一种方法论也不是开发软件的具体方法更不是一个框架或者过程 “敏捷”是一套价值观&#xff08;理念&#xff09;和原则&#xff0c;帮助团队在软…

PVE虚拟机配置文件恢复(qm list不显示虚拟机,web控制台看不到虚拟机)

本文章的目的是故障后复盘&#xff1a; 故障现象在命令行执行qm list不显示虚拟机&#xff0c;web控制台看不到虚拟机&#xff0c;网上查不到相关现象的处理办法。 处理思路&#xff1a;虚拟机还在正常工作&#xff0c;通过查看kvm进程ps aux | grep kvm&#xff0c;百度查看…

产品百度百科怎么创建?产品如何上百度百科?

百度百科作为一个权威的信息平台&#xff0c;承载着巨大的流量和曝光度。对于一个产品来说&#xff0c;能够在百度百科上拥有一席之地&#xff0c;无疑是一种极高的荣誉&#xff0c;同时也是提升品牌知名度、增加信任度的重要手段。产品百度百科不仅能够详细、全面地介绍产品信…

【学习iOS高质量开发】——熟悉Objective-C

文章目录 一、Objective-C的起源1.OC和其它面向对象语言2.OC和C语言3.要点 二、在类的头文件中尽量少引用其他头文件1.OC的文件2.向前声明的好处3.如何正确引入头文件4.要点 三、多用字面量语法&#xff0c;少用与之等价的方法1.何为字面量语法2.字面数值3.字面量数组4.字面量字…

跟着cherno手搓游戏引擎【6】ImGui和ImGui事件

导入ImGui&#xff1a; 下载链接&#xff1a; GitHub - TheCherno/imgui: Dear ImGui: Bloat-free Immediate Mode Graphical User interface for C with minimal dependencies 新建文件夹&#xff0c;把下载好的文件放入对应路径&#xff1a; SRC下的premake5.lua文件&#…

算法通关村番外篇-LeetCode编程从0到1系列四

大家好我是苏麟 , 今天带来算法通关村番外篇-LeetCode编程从0到1系列四 . 矩阵 1672. 最富有客户的资产总量 描述 : 给你一个 m x n 的整数网格 accounts &#xff0c;其中 accounts[i][j] 是第 i​​​​​ 位客户在第 j 家银行托管的资产数量。返回最富有客户所拥有的 资产…

书客Sun立式护眼台灯正式上市,技术优势全面领跑,革新行业的护眼养眼!

在当今智能化的社会&#xff0c;我们与电子屏幕的接触日益增多&#xff0c;同时&#xff0c;无需离开家门即可享受各种便捷服务。由于过度使用电子设备、与阳光的接触机会逐渐减少或其他不合理的用眼习惯导致近视问题不断加剧。 作为专业的护眼品牌&#xff0c;SUKER书客深刻认…

Python画球面投影图

天文学研究中&#xff0c;有时候需要画的并不是传统的XYZ坐标系&#xff0c;而是需要画一个形如这样子的球面投影图&#xff1a; 下面讲一下这种图怎么画 1. 首先要安装healpy包 pip install healpy 2. 然后导入包 如果之前安装过healpy&#xff0c;有的会提示不存在healpy…

matlab串口数据交互的使用

一、matlab将串口数据读取并储存到position中 delete(instrfindall);%注销系统之前已经打开的串口资源 clear s %清空s的数据 s serial(COM6,BaudRate,115200);%定义串口及波特率 fopen(s)%打开串口 fwrite(s,00AB,)%向串口写入读取电机位置指令 for i1:8 %共8个电机position…

IPv6隧道--GRE隧道

GRE隧道 通用路由封装协议GRE(Generic Routing Encapsulation)可以对某些网络层协议(如IPX、ATM、IPv6、AppleTalk等)的数据报文进行封装,使这些被封装的数据报文能够在另一个网络层协议(如IPv4)中传输。 GRE提供了将一种协议的报文封装在另一种协议报文中的机制,是一…

【UE Niagara 喷射火焰系列】06 - 制作火焰喷射过程中飞舞的火星

在上一篇博客&#xff08;【UE Niagara学习笔记】05 - 喷射火焰顶部的蓝色火焰&#xff09;的基础上继续实现喷射火焰的火星的效果。 目录 效果 步骤 一、创建材质实例 二、添加新的发射器 2.1 设置粒子材质 2.2 设置发射器持续生成粒子 2.3 设置粒子生成数量 2.4 设…

day19【LeetCode力扣】160.相交链表

day19【LeetCode力扣】160.相交链表 1.题目描述 给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点&#xff0c;返回 null 。 图示两个链表在节点 c1 开始相交**&#xff1a;** 题目数据 保证 整个链…

是时候丢掉 DDE 了

有人问我这样一个问题&#xff1a; “作为一名应用程序开发者&#xff0c;如果希望和外壳(Explorer/Shell)打交道&#xff0c;我可以直接忽略掉 DDE 吗&#xff1f;” 此问题的答案是&#xff1a;是的&#xff0c;完全没有任何问题。虽然在发明它的 16 位 Windows 协作多任务…

HubSpot电子邮件自动回复怎么设置?附注意事项!

HubSpot 提供了电子邮件自动回复的功能&#xff0c;使得企业能够更高效地管理和处理电子邮件通信。以下是关于 HubSpot 电子邮件自动回复的一些重要信息和步骤&#xff1a; 设置电子邮件自动回复的步骤&#xff1a; 登录HubSpot账户&#xff1a; 打开 HubSpot 平台并登录你的账…

数据管理系统-week2-对象数据模型

文章目录 前言一、对象的类二、关联(Association)三、 连接属性(Link Attribute)四、关联类(Association Class)五、Qualification五、Generalisation参考文献Association • Link Attribute • Association Class • Qualification • Generalization 前言 在实际的项…

Python--GIL(全局解释器锁)

在Python中&#xff0c;GIL&#xff08;全局解释器锁&#xff09;是一个非常重要的概念&#xff0c;它对Python的多线程编程有着深远的影响。GIL是Python解释器级别的锁&#xff0c;用于保证任何时刻只有一个线程在执行Python字节码。这意味着即使在多核处理器上&#xff0c;Py…

Java Web 状态管理(上) Cookie基础

Java Web 状态管理&#xff08;上&#xff09; Cookie基础知识 前言一、Cookie产生的背景&#xff08;不重要&#xff09;二、Cookie简介三、Cookie的使用场景引入添加苹果进入购物车添加香蕉进入购物车那么假设说没有Cookie正常有Cookie的情况 四、场景简单模拟创建第一个Serv…

蓝桥杯备赛day02 -- 算法训练题 拿金币Java

目录 题目&#xff1a; 问题描述 输入格式 输出格式 解题过程 第一步 定义dp数组 第二步 确定 dp 数组递推公式 第三步 dp数组的初始化 第四步 dp数组的遍历顺序 第五步 举例说明 报错&#xff1a;内存超限 用dp数组去存储位置上的金币 dp数组从二维降为一维 收获&a…

Spark详解

Spark 概念 Spark 提供了一个全面、统一的框架用于管理各种有着不同性质&#xff08;文本数据、图表数据等&#xff09;的数据集和数据源&#xff08;批量数据或实时的流数据&#xff09;的大数据处理的需求。 核心架构 Spark Core 包含 Spark 的基本功能&#xff1b;尤其是…