驾驭 Spring Boot 事件机制:8 个内置事件 + 自定义扩展实战

代码星辉·七月创作之星挑战赛 10w+人浏览 283人参与

驾驭 Spring Boot 事件机制:8 个内置事件 + 自定义扩展实战

在 Spring Boot 应用的完整生命周期中,框架为我们预埋了 8 个关键事件(Application-level & Context-level)。 理解并善用这些事件,可以在“不侵入框架、不修改源码”的前提下,注入个性化初始化、监控、清理逻辑。 本文将带你从 0 到 1 掌握事件机制,并给出可直接落地的代码模板。


一、为什么需要事件机制?

场景传统做法事件机制优势
启动时加载字典缓存CommandLineRunner无侵入、可插拔、可排序
优雅停机@PreDestroy与 Spring 生命周期同步,确保资源释放顺序
多模块解耦直接调用发布-订阅,模块间零依赖

二、Spring Boot 8 大内置事件一览

事件触发阶段典型用途监听器注册方式
ApplicationStartingEventrun() 刚被调用,日志系统尚未初始化极早期检查、初始化日志桥接SpringApplication.addListeners(...)
ApplicationEnvironmentPreparedEventEnvironment 已就绪,但 BeanDefinition 尚未加载动态修改配置源、激活 Profile同上
ApplicationContextInitializedEventApplicationContext 已创建,但尚未 refresh注册 BeanFactoryPostProcessor同上
ApplicationPreparedEventBeanDefinition 已加载,Environment 可用读取配置、校验必备属性同上
ContextRefreshedEventrefresh() 完成,所有单例已实例化缓存预热、注册监控@Component
ServletWebServerInitializedEvent内嵌容器端口已打开获取运行时端口、注册服务发现@Component
ApplicationStartedEvent容器已启动,所有 CommandLineRunner 已执行发送启动成功指标@Component
ApplicationReadyEvent同上,额外保证所有应用初始化器已完成开启流量、发送通知@Component

Spring Boot 2.x 之后新增 ApplicationStartingEventApplicationStartedEvent 等,旧版只有 5 个核心事件。


三、实战:监听 4 个高频事件

1. 启动早期动态注入配置

public class EarlyEnvInjector implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {
    @Override
    public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
        ConfigurableEnvironment env = event.getEnvironment();
        // 模拟从 Apollo/Nacos 拉取最新配置
        Map<String, Object> override = Map.of("spring.datasource.url", "jdbc:mysql://newHost/dev");
        env.getPropertySources().addFirst(new MapPropertySource("dynamic", override));
    }
}

注册方式(在 main 方法里):

SpringApplication app = new SpringApplication(DemoApp.class);
app.addListeners(new EarlyEnvInjector());
app.run(args);

2. 容器刷新后预热缓存

@Component
public class CacheWarmer implements ApplicationListener<ContextRefreshedEvent> {
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        if (event.getApplicationContext().getParent() == null) { // 防止重复执行
            DictCache.loadAll();
        }
    }
}

3. 优雅停机前释放资源

@Component
public class GracefulShutdown implements ApplicationListener<ContextClosedEvent> {
    private final ExecutorService pool = Executors.newFixedThreadPool(10);

    @Override
    public void onApplicationEvent(ContextClosedEvent event) {
        pool.shutdown();
        try {
            if (!pool.awaitTermination(10, TimeUnit.SECONDS)) {
                pool.shutdownNow();
            }
        } catch (InterruptedException e) {
            pool.shutdownNow();
        }
    }
}

4. 启动完毕发送监控告警

@Component
public class StartupReporter implements ApplicationListener<ApplicationReadyEvent> {
    @Override
    public void onApplicationEvent(ApplicationReadyEvent event) {
        InetAddress host = InetAddress.getLocalHost();
        String port = event.getApplicationContext()
                          .getEnvironment()
                          .getProperty("local.server.port");
        DingTalk.send("✅ 服务启动完成: " + host.getHostAddress() + ":" + port);
    }
}

四、扩展:自定义业务事件

1. 定义领域事件

public class OrderPaidEvent extends ApplicationEvent {
    private final Long orderId;
    private final BigDecimal amount;
    public OrderPaidEvent(Object source, Long orderId, BigDecimal amount) {
        super(source);
        this.orderId = orderId;
        this.amount = amount;
    }
    // getters ...
}

2. 发布事件

@Service
@RequiredArgsConstructor
public class OrderService {
    private final ApplicationEventPublisher publisher;

    public void pay(Long orderId) {
        // 业务逻辑...
        publisher.publishEvent(new OrderPaidEvent(this, orderId, BigDecimal.valueOf(99)));
    }
}

3. 多监听器异步消费

@Component
public class InvoiceGenerator {
    @EventListener
    @Async("invoiceTaskExecutor")   // 线程池隔离
    public void onOrderPaid(OrderPaidEvent event) {
        // 生成电子发票...
    }
}

五、最佳实践清单

  1. 顺序控制:使用 @Order 或实现 Ordered 接口。
  2. 线程安全:早期事件(如 ApplicationStartingEvent)发布时,Bean 尚未实例化,此时注册逻辑需避免依赖 IOC 容器。
  3. 条件化监听@ConditionalOnPropertyEnvironment 判断,避免在测试环境触发线上逻辑。
  4. 异步场景@Async + 自定义线程池,防止阻塞主流程。
  5. 可观测性:通过 Micrometer 记录事件处理耗时,及时发现慢监听器。

六、小结

目标推荐事件
动态修改配置ApplicationEnvironmentPreparedEvent
容器初始化后一次性任务ContextRefreshedEvent
优雅停机ContextClosedEvent
服务启动成功通知ApplicationReadyEvent
业务解耦自定义 ApplicationEvent
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

半部论语

如果觉得有帮助,打赏鼓励一下

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值