当前位置: 首页 > news >正文

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.lryc.cn/news/617337.html

相关文章:

  • 腾讯云Edgeone限时免费
  • 有序矩阵中第K小的元素+二分查找
  • 使用 Rust 创建 32 位 DLL 的完整指南
  • 数据大集网:精准获客新引擎,助力中小企业突破推广困局
  • CSPOJ:1561: 【提高】买木头
  • 请求报文和响应报文(详细讲解)
  • nomachine的安装和使用
  • 零基础学习jQuery第三天
  • 用 Python 绘制企业年度财务可视化报告 —— 从 Excel 到 9 种图表全覆盖
  • DDIA第五章:分布式数据复制中的一致性与冲突处理
  • 第5节 大模型分布式推理通信优化与硬件协同
  • 在Debian上安装MySQL
  • Excel 实战:基因表达矩阵前处理中测序符号的快速剥离方法
  • golang 基础案例_02
  • 设计模式笔记_结构型_享元模式
  • 深入解析Prompt缓存机制:原理、优化与最佳实践
  • Agent在供应链管理中的应用:库存优化与需求预测
  • Python FastAPI + React + Nginx 阿里云WINDOWS ECS部署实战:从标准流程到踩坑解决全记录
  • typecho博客设置浏览器标签页图标icon
  • 【工控】线扫相机小结 第六篇
  • uncalled4
  • 麒麟系统使用-PATH设置
  • 【接口自动化】-7- 热加载和日志封装
  • 实战:用 PyTorch 复现一个 3 层全连接网络,训练 MNIST,达到 95%+ 准确率
  • 软件测试关于搜索方面的测试用例
  • DeepCompare文件深度对比软件:权限管理与安全功能全面解析
  • Android Audio实战——获取活跃音频类型(十五)
  • 安全合规4--下一代防火墙组网
  • 企业内外网物理隔离时文件怎么传输更安全
  • ChatML vs Harmony:深度解析OpenAI全新对话结构格式的变化