题目:如何在 Spring 启动过程中做缓存预热?
难度:difficulty/medium
评分:⭐⭐⭐⭐☆(4/5)
启动过程中进行预热可以使用 CommandLineRunner 接口,实现该接口,可以在所有 bean 实例化后初始化完成后执行,可以定义一些自定义操作,比如我们要做的缓存预热,还有类似的 ApplicationRunner。
优点:CommandLineRunner/ApplicationRunner 时机准确(bean 实例化后、容器就绪前)。
不足:漏两 Runner 差异(ApplicationRunner 接收 ApplicationArguments,CommandLineRunner 接收 String[] args);漏 @PostConstruct、ApplicationReadyEvent、InitializingBean 备选。
补充:预热失败处理与启动耗时影响;SmartLifecycle 的 phase 控制。
最后更新:2026-10-27 · 基于第 1 次作答
结论:缓存预热选 ApplicationRunner/CommandLineRunner,所有 Bean 就绪后、对外服务前执行。 关键词:Bean 就绪 · run() 完成前 · @Order 排序 边界:预热要幂等容错,失败不阻断启动。
核心考点定位:考察 Spring 启动生命周期钩子,选对预热时机。面试官想排除"用 @PostConstruct 预热"的时机错误。
机制链(≥3 环):
证据锚点(≥10):
ApplicationRunner:run(ApplicationArguments) 方法CommandLineRunner:run(String[] args) 方法@PostConstruct:Bean 实例化后执行InitializingBean.afterPropertiesSet():Bean 级初始化ApplicationListener<ContextRefreshedEvent>:容器刷新ApplicationReadyEvent:应用就绪事件@Order:控制多个 Runner 顺序SmartLifecycle:phase 控制启动顺序ApplicationArguments:解析后的命令行参数SpringApplication.run():应用启动入口对比与权衡:
| 时机 | 正确性 | 依赖就绪 | 适用 |
|---|---|---|---|
| @PostConstruct | 中 | 部分 Bean | Bean 内初始化 |
| ContextRefreshedEvent | 高 | 全 Bean | 容器级预热 |
| ApplicationRunner | 高 | 全 Bean+环境 | 缓存预热(推荐) |
| ApplicationReadyEvent | 高 | 全部就绪 | 对外后预热 |
常见误区:
适用边界:Runner 是启动一次性动作;运行时缓存更新靠定时任务或事件监听,不能只靠启动预热。大缓存预热要异步或分批,避免拖慢启动。若应用是集群部署,预热还要考虑多实例同时预热对下游 DB 的冲击,必要时用预热锁或错峰。
【结论】 面试官您好,缓存预热我选 ApplicationRunner/CommandLineRunner,它们在所有 Bean 就绪后、应用对外服务前执行。
【三点】 第一,时机——@PostConstruct 是 Bean 级,此时其他 Bean 可能未就绪;Runner 在 SpringApplication.run() 完成前执行,所有 Bean 已就绪,最安全。第二,两者区别——CommandLineRunner 收 String[] args,ApplicationRunner 收解析后的 ApplicationArguments,可用 @Order 控制顺序。第三,实践——预热要幂等容错(失败记日志不阻断启动),大缓存异步或分批控制耗时,避免拖慢启动。
【边界】 当然,预热是启动时一次性动作,运行时缓存更新还得靠定时任务或监听变更事件,不能只靠启动预热。
| # | 日期 | 评分 | 本轮主要问题 | 记录 |
|---|---|---|---|---|
| 1 | 2026-10-27 | 4/5 | 漏两 Runner 差异与备选时机 | 第 1 次 |