在 Spring Boot 里,CommandLineRunner 是一个非常常用、但又容易被低估的启动钩子接口。
一、CommandLineRunner 是什么?
CommandLineRunner 是 Spring Boot 提供的一个接口:
java
public interface CommandLineRunner {
void run(String... args) throws Exception;
}
👉 只做一件事:
在 Spring Boot 应用启动完成后,执行一段代码
二、什么时候执行?(生命周期位置)
Spring Boot 启动顺序(简化版):
1. SpringApplication.run()
2. 创建 ApplicationContext
3. 扫描 Bean / 注入依赖
4. 执行 @PostConstruct
5. 执行 CommandLineRunner / ApplicationRunner
6. 启动完成(应用对外可用)
📌 关键点:
- 所有 Bean 已经初始化完成
@Autowired可以正常用- 适合做 启动后初始化逻辑
三、最基本用法
方式一:实现接口(最常见)
java
@Component
public class InitRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("Spring Boot 启动完成");
}
}
启动后控制台输出:
Spring Boot 启动完成
方式二:@Bean 方式(更灵活)
java
@Bean
public CommandLineRunner init() {
return args -> {
System.out.println("执行初始化逻辑");
};
}
适合 配置类中集中管理启动逻辑。
四、args 是什么?
args 就是 启动 JVM 时的命令行参数
bash
java -jar app.jar --env=prod --debug=true
在 run 里:
java
@Override
public void run(String... args) {
for (String arg : args) {
System.out.println(arg);
}
}
输出:
--env=prod
--debug=true
📌 注意:
- Spring 的
application.properties参数也会被解析,但这里拿到的是原始命令行
五、多个 CommandLineRunner 的执行顺序
如果你有多个 Runner:
java
@Component
@Order(1)
public class FirstRunner implements CommandLineRunner {
public void run(String... args) {
System.out.println("First");
}
}
java
@Component
@Order(2)
public class SecondRunner implements CommandLineRunner {
public void run(String... args) {
System.out.println("Second");
}
}
执行顺序:
First
Second
📌 规则:
- 数字 越小越先执行
- 也可以实现
Ordered接口
六、和 ApplicationRunner 的区别(面试常问)
| 对比项 | CommandLineRunner | ApplicationRunner |
|---|---|---|
| 参数 | String... args |
ApplicationArguments |
| 参数解析 | 需要自己拆 | 已解析(key-value) |
| 易用性 | 简单 | 更强 |
| 使用频率 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
ApplicationRunner 示例:
java
@Component
public class AppRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) {
args.getOptionNames().forEach(System.out::println);
}
}
七、常见使用场景(非常重要)
1️⃣ 初始化缓存(Redis / 本地缓存)
java
@Component
public class CacheInitRunner implements CommandLineRunner {
@Autowired
private UserService userService;
@Override
public void run(String... args) {
userService.loadAllUsersToCache();
}
}
✔ 广告 / 游戏后台非常常见
✔ 你之前提到 Redis、广告初始化,这个用法很典型
2️⃣ 启动时校验配置
java
@Override
public void run(String... args) {
if (StringUtils.isEmpty(apiKey)) {
throw new RuntimeException("API_KEY 未配置");
}
}
📌 失败就让服务启动失败
3️⃣ 初始化定时任务 / 监听器
java
@Override
public void run(String... args) {
taskManager.start();
}
4️⃣ 数据迁移 / 表结构检查(轻量级)
java
@Override
public void run(String... args) {
migrateService.checkAndFix();
}
5️⃣ 游戏 / 广告 SDK 初始化
你做广告变现,这种非常常见:
java
@Override
public void run(String... args) {
AdConfig.init();
SdkManager.load();
}
八、和 @PostConstruct 的区别(重点)
| 对比 | @PostConstruct | CommandLineRunner |
|---|---|---|
| 执行时机 | 单个 Bean 初始化后 | 全部 Bean 初始化完成 |
| 是否能注入其他 Bean | 有风险 | 安全 |
| 执行次数 | 每个 Bean 一次 | 全局一次 |
| 适合做 | Bean 内部初始化 | 系统级初始化 |
📌 经验法则:
- Bean 自己的事 → @PostConstruct
- 系统启动的事 → CommandLineRunner
九、注意事项(踩坑点)
❌ 1. 不要写阻塞死循环
java
@Override
public void run(String... args) {
while (true) {} // ❌ 服务卡死
}
✔ 如果要长任务,新开线程
❌ 2. 不要写超慢逻辑
- 会 拖慢服务启动
- Kubernetes / 云环境容易被判定启动失败
✔ 推荐做法
java
@Override
public void run(String... args) {
CompletableFuture.runAsync(() -> initHeavyTask());
}
十、一句话总结
CommandLineRunner 是 Spring Boot 提供的启动完成回调接口,用于在容器完全初始化后执行系统级初始化逻辑,常用于缓存预热、配置校验、任务启动等场景。