Spring Boot启动事件详解:类型、监听与实战应用

1. Spring Boot启动事件概述

1.1 什么是Spring Boot启动事件

在Spring Boot的应用生命周期中,从main方法执行到应用完全就绪,期间会发生一系列事件(Event)。这些事件由Spring Boot框架在特定时间点触发,用于通知系统当前运行阶段的状态,并允许我们在这些时间点插入自定义逻辑。

可以把Spring Boot的启动过程想象成一个舞台剧:

  • 灯光亮起(ApplicationStartingEvent)

  • 舞台布景准备好(ApplicationEnvironmentPreparedEvent)

  • 演员到位(ApplicationPreparedEvent)

  • 正式开演(ApplicationStartedEvent)

  • 演出完成(ApplicationReadyEvent)

  • 演出失败(ApplicationFailedEvent)

这些事件的主要作用包括:

  1. 生命周期钩子(Lifecycle Hooks):在特定启动阶段执行逻辑,如加载外部配置、初始化缓存等。

  2. 解耦:监听器与事件触发点解耦,方便扩展和维护。

  3. 可观测性:配合日志或监控,可以跟踪启动进度和状态。


1.2 事件在Spring Boot中的作用

Spring Boot事件机制主要解决了两个问题:

  1. 启动过程的扩展
    例如在Spring Context初始化前,我们就能通过事件拿到Environment,从而动态修改配置。

  2. 运行时状态监听
    例如在应用就绪后,立即启动一个异步任务,或在启动失败时发送报警信息。

常见应用场景

  • ApplicationStartingEvent阶段设置日志系统参数。

  • ApplicationEnvironmentPreparedEvent阶段加载云端配置文件。

  • ApplicationReadyEvent阶段预热数据或启动定时任务。

  • ApplicationFailedEvent阶段上报启动失败的原因。

2. Spring Boot启动事件分类

2.1 核心事件列表

Spring Boot 启动过程中会触发一系列标准事件,这些事件是按照应用生命周期的先后顺序触发的。核心事件包括:

事件类名触发时机特点
ApplicationStartingEventSpringApplication.run() 刚开始执行时,且在创建 ApplicationContext 之前最早触发的事件,可以在这里做一些全局初始化工作,如修改 Banner、初始化日志系统
ApplicationEnvironmentPreparedEvent环境变量 (Environment) 准备好,但 ApplicationContext 还未创建可以在这里读取配置文件、动态修改配置
ApplicationContextInitializedEventApplicationContext 已创建但还未进行 Bean 加载适合做一些基于容器的初始化工作
ApplicationPreparedEventApplicationContext 已刷新(Bean 定义已加载),但还未调用 refresh() 完成可以在这里对 Bean 做调整
ApplicationStartedEvent应用已启动,但 CommandLineRunner 和 ApplicationRunner 还未执行表示应用启动阶段已完成,马上要进入业务逻辑阶段
ApplicationReadyEvent所有 Runner 执行完毕,应用已完全就绪常用于启动定时任务、发通知、预热缓存
ApplicationFailedEvent启动过程中发生异常时触发常用于记录错误日志、报警、清理资源


2.2 事件触发的完整生命周期

下面是事件触发的时间线,可以帮助理解它们的先后顺序:

  1. ApplicationStartingEvent

    • SpringApplication 刚开始运行。

  2. ApplicationEnvironmentPreparedEvent

    • 环境(Environment)已准备好。

  3. ApplicationContextInitializedEvent

    • ApplicationContext 已初始化但未加载 Bean。

  4. ApplicationPreparedEvent

    • ApplicationContext 已准备好 Bean 定义,但未刷新。

  5. ApplicationStartedEvent

    • 应用启动完成,Runner 未执行。

  6. ApplicationReadyEvent

    • 应用完全就绪,Runner 已执行。

  7. ApplicationFailedEvent

    • 启动失败时触发(任何阶段出错都会触发)。

💡 可以将其记为:
Start → Env → ContextInit → Prepared → Started → Ready → Failed


2.3 事件与ApplicationContext的关系

ApplicationContext 是 Spring 容器的核心,很多事件与它的生命周期紧密相关:

  • 创建前
    ApplicationStartingEventApplicationEnvironmentPreparedEvent 发生在容器创建前,这时还没有 Bean 信息。

  • 创建中
    ApplicationContextInitializedEvent 表示容器已实例化,但 Bean 还没加载。

  • 准备就绪
    ApplicationPreparedEvent 表示 Bean 定义已加载,可以对 Bean 做最后的调整。

  • 运行阶段
    ApplicationStartedEventApplicationReadyEvent 发生在容器完全启动后,可以安全地访问 Bean。

  • 异常阶段
    ApplicationFailedEvent 可以拿到异常和容器状态,方便做错误处理。


示例代码:打印启动事件顺序
import org.springframework.boot.context.event.*;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;@Component
public class StartupEventLogger implements ApplicationListener<ApplicationEvent> {@Overridepublic void onApplicationEvent(ApplicationEvent event) {System.out.println("触发事件: " + event.getClass().getSimpleName());}
}

运行 Spring Boot 应用时,你会在控制台看到事件触发的顺序,从而直观了解生命周期。

3. 事件监听方式详解

Spring Boot 支持多种方式监听启动事件,主要有三种常用手段:

  1. @EventListener 注解(简单优雅,推荐大多数场景)

  2. 实现 ApplicationListener 接口(更传统、可精细控制)

  3. 自定义 SpringApplicationRunListener(启动最早阶段可用)


3.1 使用 @EventListener 注解

原理
@EventListener 是 Spring 4.2 引入的事件监听方式,基于反射,方法签名中声明的事件类型会被自动匹配。
它最大的好处是:不需要实现接口,代码更简洁。

使用步骤

  1. 在 Spring Bean 中编写一个普通方法。

  2. 方法上添加 @EventListener 注解。

  3. 方法参数为需要监听的事件类型。

示例:监听 ApplicationReadyEvent

import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;@Component
public class ReadyEventListener {@EventListenerpublic void handleReady(ApplicationReadyEvent event) {System.out.println("应用已就绪,可以开始执行任务!");// 比如预热缓存}
}

优点

  • 代码简洁,灵活。

  • 支持条件匹配(@EventListener(condition = ""))。

缺点

  • 无法在 Spring 容器完全创建前使用(因为需要依赖 Bean)。


3.2 实现 ApplicationListener 接口

原理
ApplicationListener 是 Spring 事件机制的传统接口,早于 @EventListener 出现。
通过实现该接口,可以监听指定事件类型,支持类型安全和较高性能。

使用步骤

  1. 实现 ApplicationListener<T> 接口,T 为事件类型。

  2. 将监听器注册为 Spring Bean(@Component 或手动注册)。

示例:监听 ApplicationStartedEvent

import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;@Component
public class StartedEventListener implements ApplicationListener<ApplicationStartedEvent> {@Overridepublic void onApplicationEvent(ApplicationStartedEvent event) {System.out.println("应用启动完成,Runner 还未执行!");}
}

优点

  • 类型安全(编译期即可检查事件类型)。

  • 性能好(无反射调用)。

缺点

  • 代码稍显冗长。

  • 只能监听单一类型(要监听多个事件需多实现)。


3.3 自定义 SpringApplicationRunListener

原理
SpringApplicationRunListener 是 Spring Boot 提供的一个扩展点,用于在SpringApplication.run() 的各个阶段执行逻辑。
它的生命周期比普通事件监听器更早,甚至可以在 ApplicationContext 创建前执行逻辑。

使用步骤

  1. 实现 SpringApplicationRunListener 接口。

  2. META-INF/spring.factories 文件中注册。

  3. 在接口方法中编写启动阶段逻辑。

示例:记录启动流程

import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringApplicationRunListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;public class MyRunListener implements SpringApplicationRunListener {public MyRunListener(SpringApplication application, String[] args) {// 必须有这个构造器}@Overridepublic void starting() {System.out.println("【RunListener】应用启动中...");}@Overridepublic void environmentPrepared(ConfigurableEnvironment environment) {System.out.println("【RunListener】环境已准备好");}@Overridepublic void contextPrepared(ConfigurableApplicationContext context) {System.out.println("【RunListener】上下文已创建");}@Overridepublic void started(ConfigurableApplicationContext context) {System.out.println("【RunListener】应用已启动");}@Overridepublic void ready(ConfigurableApplicationContext context) {System.out.println("【RunListener】应用已就绪");}@Overridepublic void failed(ConfigurableApplicationContext context, Throwable exception) {System.out.println("【RunListener】启动失败:" + exception.getMessage());}
}

META-INF/spring.factories 文件注册

org.springframework.boot.SpringApplicationRunListener=\
com.example.listener.MyRunListener

优点

  • 生命周期最早,可以做环境准备、日志初始化等。

  • 不依赖 Spring 容器。

缺点

  • 配置复杂(需在 spring.factories 注册)。

  • 主要用于框架级扩展,不推荐业务代码中频繁使用。

4. 事件的实际应用场景

Spring Boot 启动事件的真正价值,在于它可以帮我们在恰当的时间点执行关键任务,从而让系统启动更加平滑、可控。
下面我们结合常见业务场景,给出可以直接运行的示例。


4.1 初始化外部资源(如数据库连接)

场景
有些外部资源(如数据库连接池、消息队列客户端、第三方 API)需要在应用启动早期就准备好。
如果等到业务逻辑调用时再初始化,可能会导致首个请求延迟较高。

选择事件

  • ApplicationStartedEvent
    因为此时容器已准备好,可以安全获取 Bean,但业务逻辑还未开始执行。

示例:初始化数据库连接池

import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;@Component
public class DatabaseInitListener implements ApplicationListener<ApplicationStartedEvent> {@Overridepublic void onApplicationEvent(ApplicationStartedEvent event) {System.out.println("初始化数据库连接池...");// 模拟初始化try {Thread.sleep(2000);System.out.println("数据库连接池初始化完成!");} catch (InterruptedException e) {e.printStackTrace();}}
}

4.2 动态加载配置文件

场景
有时我们需要在应用启动前,从远程配置中心(如 Nacos、Apollo)拉取最新配置。
这必须发生在 Spring 环境 (Environment) 准备好之后,但在容器创建前。

选择事件

  • ApplicationEnvironmentPreparedEvent
    此时可以安全读取/修改 Environment

示例:加载远程配置

import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.stereotype.Component;@Component
public class RemoteConfigListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {@Overridepublic void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {ConfigurableEnvironment env = event.getEnvironment();System.out.println("从远程配置中心加载配置...");// 模拟加载env.getSystemProperties().put("custom.config.source", "remote");}
}

4.3 预热缓存数据

场景
在一些高并发系统中,首次请求往往需要加载大量数据。
为了减少冷启动延迟,我们可以在应用就绪 (ApplicationReadyEvent) 时提前加载缓存。

选择事件

  • ApplicationReadyEvent
    此时所有 Bean 已创建,Runner 已执行,系统处于稳定可用状态。

示例:缓存预热

import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;@Component
public class CachePreheatListener {@EventListenerpublic void onReady(ApplicationReadyEvent event) {System.out.println("应用就绪,开始预热缓存...");// 模拟缓存预热try {Thread.sleep(1000);System.out.println("缓存预热完成!");} catch (InterruptedException e) {e.printStackTrace();}}
}

4.4 处理启动失败事件

场景
在生产环境中,启动失败可能意味着业务中断,需要第一时间报警或回滚操作。

选择事件

  • ApplicationFailedEvent
    任何阶段发生异常都会触发。

示例:启动失败报警

import org.springframework.boot.context.event.ApplicationFailedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;@Component
public class StartupFailedListener implements ApplicationListener<ApplicationFailedEvent> {@Overridepublic void onApplicationEvent(ApplicationFailedEvent event) {System.err.println("应用启动失败,原因:" + event.getException().getMessage());// 模拟发送告警邮件}
}

5. 事件监听的最佳实践

事件监听是一个很灵活的工具,但如果使用不当,可能会导致启动变慢、逻辑混乱,甚至事件丢失。
下面从三个方面总结最佳实践。


5.1 事件执行顺序的控制

有时我们需要确保多个监听器按指定顺序执行,比如:

  • 先加载配置,再初始化数据库

  • 先连接第三方服务,再启动业务逻辑

Spring 提供了两种方式来控制事件监听器的执行顺序:

方式一:@Order 注解
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;@Component
public class FirstReadyTask {@EventListener@Order(1) // 数字越小,优先级越高public void run(ApplicationReadyEvent event) {System.out.println("先执行任务A");}
}@Component
public class SecondReadyTask {@EventListener@Order(2)public void run(ApplicationReadyEvent event) {System.out.println("再执行任务B");}
}
方式二:实现 SmartApplicationListener 接口

SmartApplicationListener 提供了 getOrder() 方法,适合需要动态控制顺序的场景。


5.2 异常处理与日志记录

如果监听器抛出未捕获的异常,会影响启动流程,甚至中断应用。
建议:

  • 捕获并记录异常,不要让它向外抛。

  • 对启动非关键任务,异常时记录日志但不阻断启动。

  • 对关键任务(如加载核心配置),异常时可直接退出启动,防止系统处于错误状态。

示例:异常处理模板
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;@Component
public class SafeStartupListener implements ApplicationListener<ApplicationStartedEvent> {@Overridepublic void onApplicationEvent(ApplicationStartedEvent event) {try {// 执行关键逻辑System.out.println("执行关键启动任务");} catch (Exception e) {// 记录错误System.err.println("启动任务失败:" + e.getMessage());// 选择:中断启动// System.exit(1);}}
}

5.3 性能优化建议

在高并发、启动频繁的系统(如微服务集群)中,启动事件的执行效率很重要。

优化建议

  1. 避免长耗时操作
    如果必须执行耗时任务(如加载大文件、调用慢API),应考虑放到异步线程中执行:

    import org.springframework.boot.context.event.ApplicationReadyEvent;
    import org.springframework.context.event.EventListener;
    import org.springframework.scheduling.annotation.Async;
    import org.springframework.stereotype.Component;@Component
    public class AsyncCachePreheat {@Async@EventListenerpublic void onReady(ApplicationReadyEvent event) {System.out.println("异步预热缓存...");}
    }
    
  2. 减少外部依赖的启动阻塞
    例如远程配置中心不可用时,可以加载本地备份配置,避免启动失败。

  3. 使用条件监听
    @EventListener 支持 condition 属性,只在满足条件时执行监听器:

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

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

相关文章

Python闭包详解:理解闭包与可变类型和不可变类型的关系

一、定义闭包&#xff08;Closure&#xff09; 指的是一个函数对象&#xff0c;即使其外部作用域的变量已经不存在了&#xff0c;仍然能访问这些变量。简单来说&#xff0c;闭包是由函数及其相关的环境变量组成的实体。def outer():x 10def inner():print(x)return innerf ou…

BotCash:GPT-5发布观察 工程优化的进步,还是技术突破的瓶颈?

BotCash&#xff1a;GPT-5发布观察 工程优化的进步&#xff0c;还是技术突破的瓶颈&#xff1f; 在GPT-4以多模态能力震撼业界的一年后&#xff0c;GPT-5的亮相显得有些“平静”。当人们期待着又一场颠覆性技术革命时&#xff0c;这场发布会更像是给大模型技术按下了“精细打磨…

AJAX学习(2)

目录 一.XMLHttpRequest 二.XMLHttpRequest——查询参数 三.案例——地区查询 四.XMLHttpRequest_数据提交 五.Promise 六.Promise三种状态 七.PromiseeeXHR获取省份列表&#xff08;案例&#xff09; 八.封装-简易axios-获取省份列表 九.封装-简易axios-获取地区列表 …

解决 pip 安装包时出现的 ReadTimeoutError 方法 1: 临时使用镜像源(单次安装)

解决 pip 安装包时出现的 ReadTimeoutError 当您在使用 pip 安装 Python 包时遇到 pip._vendor.urllib3.exceptions.ReadTimeoutError: HTTPSConnectionPool(hostfiles.pythonhosted.org, port443): Read timed out. 错误时&#xff0c;这通常是由于网络问题导致的连接超时。P…

Linux下使用Samba 客户端访问 Samba 服务器的配置(Ubuntu Debian)

在 Linux 系统中&#xff0c;Samba 提供了与 Windows 系统文件共享的便利方式。本文将详细介绍在 Ubuntu 和 Debian 系统下如何安装 Samba 客户端、访问共享资源&#xff0c;并实现远程目录挂载和开机自动挂载。 文章参考自&#xff08;感谢分享&#xff09;&#xff1a;https…

解决dedecms文章默认关键字太短的问题

在管理文章或软件的时候&#xff0c;大家在添加关键字和内容摘要的时候&#xff0c;是不是对这样的情况感到比较的郁闷&#xff0c;我的关键字设定的明明非常的好&#xff0c;可是添加或修改后&#xff0c;会被无缘无故的截去很多&#xff0c;想必大家也都非常的明白&#xff0…

K8s-kubernetes(二)资源限制-详细介绍

K8s如何合理规定对象资源使用 基本概念 Kubernetes中&#xff0c;占用资源的最小单元为单个PodKubernetes中&#xff0c;资源占用主要针对服务器的CPU、内存 为什么要做资源限制 对于Kubernetes集群而言&#xff0c;所有Pod都会占用K8s集群所在服务器的资源&#xff0c;如果不做…

量子神经网络:从NISQ困境到逻辑比特革命的破局之路

——解析2025千比特时代开发者的机遇与行动框架 引言:量子计算的“20比特魔咒”与千比特悖论 当开发者被建议“避免在>20量子比特电路训练”时,富士通却宣布2025年实现10,000物理比特系统。这一矛盾揭示了量子计算从NISQ时代向FTQC时代跃迁的核心逻辑:千比特突破非为直接…

react+vite-plugin-react-router-generator自动化生成路由

前言&#xff1a;react项目实际使用中有很多提升性能与功能的插件&#xff0c;今天来说一说vite里面提供的vite-plugin-react-router-generator&#xff0c;他主要提供了自动生成路由的功能&#xff0c;配合我们的loadable/component可以实现路由的懒加载与统一管理。1、实现效…

服务器查看 GPU 占用情况的方法

在 Linux 系统中查看 GPU 占用情况&#xff0c;主要取决于你的 GPU 类型&#xff08;NVIDIA/AMD&#xff09;&#xff0c;以下是常用方法&#xff1a; 一、NVIDIA GPU&#xff08;最常用&#xff0c;如 RTX 系列、Tesla 系列&#xff09; 使用 NVIDIA 官方工具 nvidia-smi&…

【Docker实战进阶】Docker 实战命令大全

Docker 实战命令大全 Docker 实战场景&#xff0c;以 Nginx 为核心示例&#xff0c;梳理容器生命周期、镜像管理、网络配置、数据持久化及 Compose 编排的核心命令与最佳实践。 一、容器生命周期管理 1. 基础生命周期命令 docker run - 创建并启动容器 核心功能&#xff1a;基于…

PyCharm 2025.2:面向工程师的 AI 工具

引言 随着人工智能技术的快速发展&#xff0c;AI 工程师对开发工具的需求也在不断提升。PyCharm 2025.2 版本带来了革命性的 AI 工具包&#xff0c;将 AI 开发所需的实验、调试、评估和部署功能原生集成到 IDE 中。这一重大更新不仅提升了开发效率&#xff0c;也为 AI 工程师提…

爬虫逆向--Day15--核心逆向案例2(Python逆向实现请求加密、请求堆栈、拦截器关键字)

一、逆向案例之Python逆向实现请求加密//具体代码如下 function l(t, e) {return t.toString().toUpperCase() > e.toString().toUpperCase() ? 1 : t.toString().toUpperCase() e.toString().toUpperCase() ? 0 : -1}function u(t) {for (var e Object.keys(t).sort(l)…

时序数据库市场前景分析

1. 引言随着物联网&#xff08;IoT&#xff09;、工业互联网、金融科技、智慧城市等领域的快速发展&#xff0c;数据呈现爆发式增长&#xff0c;其中时间序列数据&#xff08;Time-Series Data&#xff09;占据了重要地位。时序数据库&#xff08;Time-Series Database, TSDB&a…

【网络安全测试】Burp Suite使用指导、配置及常见问题介绍(有关必回)

Burp Suite 是**渗透测试领域事实上的标准工具**&#xff0c;尤其擅长Web应用与API安全测试。针对AI系统&#xff0c;它主要用于测试模型API、管理后台等Web接口。以下是专业级使用指南&#xff1a;---### **一、 核心模块与功能概览**| **模块** | **核心功能** | **AI测试重点…

iOS 26 一键登录失效:三大运营商 SDK 无法正常获取手机号

近期&#xff0c;不少开发者和用户反馈&#xff0c;在升级到 iOS 26 系统后&#xff0c;App 内的 一键登录功能无法正常使用。无论是移动、电信还是联通的 SDK&#xff0c;都会出现无法获取手机号的情况&#xff0c;导致用户需要改用短信验证码或手动输入手机号完成登录。问题现…

OpenLayers与Vue.js结合实现前端地图应用

OpenLayers与Vue.js结合实现前端地图应用 下面我将为您展示如何将OpenLayers与Vue.js结合创建一个功能丰富的前端地图应用。这个教程包含了基础地图展示、标记点、地图控件以及交互功能。 实现结果 实现思路 在Vue项目中集成OpenLayers库创建基础地图视图和OSM图层添加标记点…

VisDrone数据集,专为无人机视觉任务打造

在农业巡查、环保监测、安防布控等广阔天地&#xff0c;无人机&#xff08;UAV&#xff09;早已超越了“拍照打卡”的酷炫标签&#xff0c;成为不可或缺的智能之眼。然而&#xff0c;当计算机视觉模型从地面“抬头”望向无人机视角时&#xff0c;迎接它的却是截然不同的挑战&am…

【Python】Python 函数基本介绍(详细版)​

Python 函数基本介绍&#xff08;详细版&#xff09;​ 文章目录Python 函数基本介绍&#xff08;详细版&#xff09;​前言一、函数的创建​1.1 函数名的命名规则​1.2 函数的创建​1.3 函数的调用​二、函数的参数​2.1 形参和实参​2.2 位置参数​2.3 关键字参数​2.4 默认参…

【前端Vue】log-viewer组件的使用技巧

目录 修改行号和组件的样式 修改高亮显示的内容和颜色 **log-viewer组件合集** 【前端Vue】如何优雅地展示带行号的日志文件或文本内容&#xff08;log-viewer组件的使用&#xff09; 【前端Vue】使用log-viewer组件时的踩坑记录 【前端Vue】log-viewer组件的使用技巧 【前…