Skip to content

如何在 SpringBoot 启动时执行特定代码?有哪些方式? ​

  • 标签:Spring Boot、后端、Spring

🏛 业务架构师 ​

Spring Boot 启动时执行特定代码的方式按执行时机排列:

  1. @PostConstruct:Bean 依赖注入完成后立即执行——只适合轻量校验(必填配置检查),不要做重 IO(会拖慢启动);
  2. ApplicationRunner / CommandLineRunner:容器就绪后、ApplicationReadyEvent 前执行——业务初始化的标准位置(缓存预热、字典加载、注册元数据)。多个 Runner 用 @Order 控制顺序;重活建议异步化 + readiness 就绪后放流量,避免拉长启动窗口;
  3. ApplicationListener<ApplicationReadyEvent>:全就绪后触发,语义上最适合"对外宣告可用前"的最后动作;
  4. InitializingBean / init-method:效果同 @PostConstruct,但耦合框架接口,不如注解优雅;
  5. @EventListener(ContextRefreshedEvent):容器刷新完成时——注意父子容器或上下文刷新可能触发多次,要幂等;
  6. ApplicationStartedEvent / SmartInitializingSingleton:所有单例就绪后的扩展点,框架或平台组件用得多。

架构师视角的选择原则:

  • 初始化逻辑分两类:"不成功就不能服务"的(关键字典)同步做 + 失败快速终止启动;"锦上添花"的(预热、统计)异步后台做;
  • 长耗时初始化与 readiness 探针联动:K8s 场景 readiness 通过前完成关键路径,否则扩容的新 Pod 带着冷缓存接流量,发布事故的经典形态;
  • 启动钩子里的异常策略要明确:fail-fast 还是降级,不允许"初始化失败但服务照常接流量"的静默半残状态。

🏗 系统架构师 ​

按执行时点选(这是关键,不是罗列 API):

方式时点适用
@PostConstructBean 初始化(早)本 Bean 依赖就绪即可
InitializingBean/init-method同上(接口耦合,少用)遗留代码
ApplicationRunner/CommandLineRunnerrefresh 后、Ready 前大部分初始化的默认选择,支持 @Order
ApplicationReadyEvent就绪事件(最后)重预热:缓存、连接池、JIT
SmartLifecycle生命周期相位启停对称的资源(MQ 消费)

我的「启动预算」管理:

  1. 初始化分必需(Ready 前完成,阻塞 readiness 探针)与可延迟(Ready 后后台预热)两档,重活后移,扩容速度优先;
  2. 预热动作:连接池预建、本地缓存加载、HTTP 客户端连接预热——目标是第一个生产请求不承担冷启动成本;
  3. 失败策略:必需初始化失败就快速失败退出(K8s 重建),可延迟失败降级加告警;
  4. 多 Runner 依赖顺序用 @Order 显式声明。

💻 全栈开发专家 ​

按执行时机排五种:

java
// 1. @PostConstruct:Bean 初始化期,容器没 refresh 完,适合本 Bean 资源校验
@Component
public class DictCache {
    @PostConstruct
    public void check() { Assert.notEmpty(cache, "字典未加载,禁止启动"); }
}

// 2. InitializingBean#afterPropertiesSet / @Bean(initMethod):同阶段,历史风味

// 3. CommandLineRunner / ApplicationRunner:容器 refresh 完成后执行,
//    参数分别是命令行原始 args 和解析后的 ApplicationArguments
@Component
public class WarmupRunner implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) {
        cacheService.preload();              // 常驻缓存预热
    }
}

// 4. @EventListener(ApplicationReadyEvent.class):就绪事件,语义最明确
// 5. SmartLifecycle / @DependsOn:精细控制 Bean 启停顺序

我的选型习惯:配置/资源合法性校验放 @PostConstruct(带病启动不如早死,抛异常直接中断启动);重活(缓存预热、证书加载)放 ApplicationRunner 或 ReadyEvent,且可异步——K8s 里启动慢会被 liveness 杀掉,预热要挪到 readiness 通过之后。多个 Runner 用 @Order 定序。

细节:Runner 抛异常会中断启动,可失败的旁路初始化要 try-catch + 告警,别把核心服务拖死。