Skip to content

Spring Boot 启动钩子

是什么

Spring Boot 提供两个等价的「启动完成回调」接口,让业务能在「容器装配完成之后、SpringApplication.run() 返回之前」做一些初始化工作,比如建索引、预热缓存、扫描注解。

接口入参用途场景
ApplicationRunnerApplicationArguments(自带 --key=value 解析)关心命令行参数时优先选这个
CommandLineRunnerString[] args(原始数组)只需要原始 main args 时使用

两者执行时机一致;如果同时存在多个,可通过 @Order 或实现 Ordered 控制顺序,数值小的先执行。

启动顺序

SpringApplication.run()
  ├─ refresh()                       ← 容器装配(Bean 创建、依赖注入)
  ├─ afterRefresh()
  ├─ callRunners()                   ← ApplicationRunner / CommandLineRunner 在这里执行
  └─ 返回 ApplicationContext

所以 Runner 里能拿到任何已注册的 Bean,但还开始接收 HTTP 请求。

怎么用

java
@Component
@RequiredArgsConstructor
public class DemoInitializer implements ApplicationRunner {

    private final SomeBean bean;  // 任意已注册 Bean 都可注入

    @Override
    public void run(ApplicationArguments args) {
        // args.getOptionNames() → 启动参数 key 列表
        // args.getOptionValues("env") → 对应 value 列表
        bean.doInit();
    }
}

本框架怎么用

DdlInitializer 就是一个 ApplicationRunner,依赖三件事:

java
@Component
@RequiredArgsConstructor
public class DdlInitializer implements ApplicationRunner {

    private final ClazzScanner scanner;
    private final ModelRegistry registry;
    private final BeanFactory beanFactory;   // ← 用来取 AutoConfigurationPackages

    @Override
    public void run(ApplicationArguments args) {
        String[] scanPackages = fetchScanPackages();
        Set<Class<?>> classes = scanner.scan(scanPackages);
        for (Class<?> clazz : classes) {
            ModelDefinition definition = parseModel(clazz);
            registry.register(definition);
        }
    }
}

它没读 args,但选 ApplicationRunner 而非 CommandLineRunner,是为后续阶段(SQL 生成、DDL 执行)可能要根据 --ddl.mode=create-drop 这类命令行参数分流留个口子。

→ 详细见 《扫描机制 · 时序图》