视频内容摘要:Spring Boot 启动如何从 90 秒优化到 15 秒以内
摘要说明:本摘要基于视频画面字幕与关键帧整理。视频主题是 Spring Boot 应用启动耗时优化,核心观点是:不要一上来盲目"加内存、换 SSD、改启动参数",而是先用工具定位启动链路中真正耗时的阶段,再针对性优化。
1. 视频核心结论
这个视频讲的是一个典型面试题/项目优化题:
Spring Boot 应用启动很慢,比如 90 秒,如何优化到 15 秒以内?
视频给出的答案不是直接罗列优化项,而是强调:
- 先诊断,再优化:先确认慢在哪里,是 Bean 初始化慢、数据源连接慢,还是启动后置任务阻塞。
- 用启动耗时数据说话 :通过 Spring Boot Actuator 的
/actuator/startup或 APM 查看各阶段耗时。 - 针对大头优化:重点处理 Bean 扫描/初始化、数据源连接池初始化、ApplicationRunner/监听器同步任务。
- 最终优化链路 :案例中启动时间大致从 78 秒 → 55 秒 → 28 秒 → 18 秒 → 12 秒,达到 15 秒以内。
2. 面试回答思路
错误回答示例
视频开头提到,一些候选人会直接回答:
- 加内存
- 换 SSD
- 减少启动参数
- 减少一些
@Component扫描
这些回答不是完全错,但问题在于:没有先定位瓶颈,属于拍脑袋优化。
推荐回答框架
更好的回答应该是:
我不会直接说改哪个配置,而是先接入启动耗时分析,比如 Spring Boot Actuator 的
/actuator/startup或 APM,确认启动慢到底慢在 Bean 初始化、数据源初始化、配置加载,还是自定义 Runner/Listener。然后针对耗时最大的阶段逐项优化。
3. 启动耗时诊断方式
视频建议使用:
text
Spring Boot Actuator /actuator/startup
它可以看到 Spring Boot 启动过程中各阶段的耗时,帮助判断瓶颈。
视频案例中看到的耗时大致是:
| 阶段 | 耗时 | 问题说明 |
|---|---|---|
| Bean 初始化 | 52 秒 | Bean 数量太多,启动时全部实例化 |
| 数据源连接 | 18 秒 | 连接池启动时预创建数据库连接 |
| 自定义 ApplicationRunner | 6 秒 | 启动时同步加载配置文件,阻塞主线程 |
| 其他 | 2 秒 | 其他启动开销 |
这个数据说明:Bean 初始化是最大头,占了绝大部分时间。
4. 第一类优化:减少不必要的 Bean 扫描与初始化
问题原因
Spring Boot 启动时会扫描并初始化大量组件,例如:
@Component@Service@Configuration@Repository
项目越大、依赖包越多、自动配置越多,启动时需要实例化的 Bean 就越多。
视频中的例子是:
当前项目是订单服务,但
pom中引入了用户服务 SDK,SDK 里带了一堆@Configuration,订单服务根本用不到这些 Bean,但启动时仍然会初始化。
优化方式
可以通过 @ComponentScan 的 excludeFilters 排除无关包或无关配置。
示例:
java
@SpringBootApplication
@ComponentScan(
basePackages = "com.example",
excludeFilters = {
@ComponentScan.Filter(
type = FilterType.REGEX,
pattern = "com.example.user.sdk.*"
)
}
)
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
}
视频中的优化效果
| 优化前 | 优化后 |
|---|---|
| Bean 数量约 1200 个 | Bean 数量约 800 个 |
| 启动时间 78 秒 | 启动时间 55 秒 |
注意点
排除 Bean 不能盲目操作,需要确认:
- 被排除的包确实不是当前服务必须依赖;
- 不会误删核心
Configuration; - 测试环境和预发环境需要完整回归;
- 对公共 SDK 最好拆分 starter 或控制自动配置条件,而不是所有服务无差别加载。
5. 第二类优化:开启 Bean 懒加载
核心配置
properties
spring.main.lazy-initialization=true
原理
开启懒加载后,Bean 不再全部在应用启动阶段创建,而是在第一次真正使用时才创建。
视频中的类比是:
餐厅开张时,不是一开门就把 100 道菜全部做好,而是客人点什么再做什么。
视频中的优化效果
开启懒加载后,大量非启动必要 Bean 被推迟初始化:
| 阶段 | 启动时间 |
|---|---|
| 排除无关 Bean 后 | 55 秒 |
| 开启懒加载后 | 28 秒 |
重要风险:循环依赖可能延迟暴露
视频特别强调了懒加载的坑:
如果 Bean 之间存在循环依赖,懒加载可能不会在启动时报错,而是在某个接口第一次被调用时才报错,这比启动时报错更危险。
原因是:
- 正常启动时,Spring 会在 Bean 创建过程中发现循环依赖;
- 懒加载模式下,很多 Bean 启动时没有创建;
- 依赖问题可能被推迟到运行时才暴露。
正确做法
- 先关闭懒加载;
- 正常启动一次;
- 检查日志里有没有循环依赖告警;
- 有问题先解决循环依赖;
- 再开启懒加载。
6. 第三类优化:调整 HikariCP 连接池初始化
问题原因
视频中提到:
HikariCP 默认启动时就会创建连接池,比如
minimum-idle默认值为 10,意味着启动阶段立刻创建 10 个数据库连接。如果数据库响应慢,比如跨机房、网络抖动,这一步就可能拖慢几秒甚至十几秒。
优化配置
properties
spring.datasource.hikari.minimum-idle=0
含义是:
启动时不提前创建空闲连接,第一个请求来了再按需创建连接。
视频中的优化效果
| 阶段 | 启动时间 |
|---|---|
| 开启懒加载后 | 28 秒 |
| 调整 HikariCP 后 | 18 秒 |
注意点
这个配置适合启动速度敏感的服务,但需要结合业务判断:
- 如果服务刚启动就有高并发流量,完全不预热连接可能导致首批请求略慢;
- 可以结合预热接口、灰度发布、就绪探针来平衡启动速度和首个请求耗时;
- 如果数据库连接慢的根因是网络、DNS、跨机房,也应该同步排查基础设施问题。
7. 第四类优化:ApplicationRunner / 启动监听器异步化
问题原因
视频中最后发现:
有一个
ApplicationRunner在启动时加载本地配置文件,执行读文件、解析、校验等操作,耗时 6 秒。但这份配置不是接口启动必须依赖的。
如果这些逻辑写在:
ApplicationRunnerCommandLineRunnerApplicationListener@PostConstruct
并且同步执行,就会阻塞 Spring Boot 主启动线程。
优化方式
将非核心路径的启动任务改成异步执行。
示例:
java
@EnableAsync
@Configuration
public class AsyncConfig {
}
java
@Component
public class LocalConfigWarmupRunner implements ApplicationRunner {
private final LocalConfigService localConfigService;
public LocalConfigWarmupRunner(LocalConfigService localConfigService) {
this.localConfigService = localConfigService;
}
@Async
@Override
public void run(ApplicationArguments args) {
localConfigService.loadAndValidate();
}
}
更稳妥的企业级做法是使用独立线程池:
java
@Configuration
@EnableAsync
public class AsyncExecutorConfig {
@Bean("startupTaskExecutor")
public Executor startupTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(4);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("startup-task-");
executor.initialize();
return executor;
}
}
java
@Async("startupTaskExecutor")
public void loadAndValidate() {
// 读取文件、解析、校验、缓存预热等非核心启动任务
}
视频中的优化效果
| 阶段 | 启动时间 |
|---|---|
| 调整数据源后 | 18 秒 |
| Runner 异步化后 | 12 秒 |
8. 完整优化链路复盘
视频中的完整优化链路可以总结为:
| 步骤 | 优化动作 | 启动耗时变化 |
|---|---|---|
| 0 | 初始状态,存在大量 Bean、数据源预连接、Runner 同步阻塞 | 78 秒 |
| 1 | 排除无关 SDK / 无关配置类,减少 Bean 数量 | 78 秒 → 55 秒 |
| 2 | 开启 spring.main.lazy-initialization=true |
55 秒 → 28 秒 |
| 3 | 设置 spring.datasource.hikari.minimum-idle=0 |
28 秒 → 18 秒 |
| 4 | 将非必要 ApplicationRunner 改为异步执行 |
18 秒 → 12 秒 |
最终启动耗时进入 15 秒以内。
9. 可以沉淀成企业级启动优化规范
9.1 启动耗时必须可观测
建议接入:
- Spring Boot Actuator
/actuator/startup - APM 启动链路追踪
- 应用启动日志分段耗时打印
- Bean 数量统计
- 数据源初始化耗时统计
- Runner / Listener 执行耗时统计
示例代码:
java
public static void main(String[] args) {
SpringApplication application = new SpringApplication(OrderApplication.class);
application.setApplicationStartup(new BufferingApplicationStartup(2048));
application.run(args);
}
Actuator 暴露配置示例:
properties
management.endpoints.web.exposure.include=startup,health,info
9.2 公共 SDK 不要无脑自动装配
公共 SDK、starter、组件包应该遵循:
- 按需引入;
- 按条件装配;
- 不要让订单服务加载用户服务、营销服务、报表服务的一堆无关 Bean;
- 对自动配置类加条件注解。
示例:
java
@Configuration
@ConditionalOnProperty(prefix = "user.sdk", name = "enabled", havingValue = "true")
public class UserSdkAutoConfiguration {
}
9.3 启动任务必须分级
可以把启动任务分成三类:
| 类型 | 是否阻塞启动 | 示例 |
|---|---|---|
| 核心任务 | 必须阻塞 | 初始化路由、加载核心配置、校验关键依赖 |
| 可延迟任务 | 不应阻塞 | 缓存预热、非核心字典加载、本地文件预解析 |
| 后台任务 | 异步执行 | 定时报表初始化、历史数据扫描、非实时同步 |
原则:
接口可用之前必须完成的任务才允许阻塞启动;其他任务尽量异步化或延迟执行。
10. 面试版标准回答
如果面试官问:
Spring Boot 启动 90 秒,如何优化到 15 秒?
可以这样回答:
我会先做启动耗时诊断,而不是直接盲目调参数。比如接入 Spring Boot Actuator 的
/actuator/startup或 APM,看启动时间主要消耗在哪些阶段。常见大头有三个:第一是 Bean 扫描和初始化,第二是数据源连接池初始化,第三是 ApplicationRunner、CommandLineRunner 或监听器里同步执行了耗时任务。如果发现 Bean 初始化很慢,我会先排查是否引入了无关 SDK 或无关
@Configuration,通过拆分 starter、条件装配或@ComponentScan excludeFilters减少无效 Bean。然后根据业务情况开启spring.main.lazy-initialization=true,把非必要 Bean 延迟到首次使用时再初始化,但开启前要先检查循环依赖,避免问题延迟到运行时暴露。如果数据源初始化慢,我会检查 HikariCP 连接池配置,例如将
spring.datasource.hikari.minimum-idle调低甚至设为 0,避免启动阶段预创建大量连接。最后检查ApplicationRunner、CommandLineRunner、ApplicationListener中是否有读文件、远程调用、缓存预热等非核心操作,把它们改成异步执行或延后执行。整体思路是:先可观测定位瓶颈,再按 Bean、数据源、启动任务三个方向逐项优化,并用数据验证优化效果。
11. 一句话总结
这个视频真正想表达的是:
Spring Boot 启动优化不是背配置,而是先定位耗时,再针对 Bean 初始化、数据源连接、启动任务阻塞这三个大头逐步优化。