Spring Boot 启动速度优化:你真确定慢的是“启动”吗?
🏆本文收录于《滚雪球学SpringBoot 3》:
https://blog.csdn.net/weixin_43970743/category_12795608.html,专门攻坚指数提升,本年度国内最系统+最专业+最详细(永久更新)。
本专栏致力打造最硬核 SpringBoot3 从零基础到进阶系列学习内容,🚀均为全网独家首发,打造精品专栏,专栏持续更新中…欢迎大家订阅持续学习。 如果想快速定位学习,可以看这篇【SpringBoot3教程导航帖】https://blog.csdn.net/weixin_43970743/article/details/151115907,你想学习的都被收集在内,快速投入学习!!两不误。
若还想学习更多,可直接前往《滚雪球学SpringBoot(全版本合集)》:https://blog.csdn.net/weixin_43970743/category_11599389.html,涵盖SpringBoot所有版本教学文章。
演示环境说明:
- 开发工具:IDEA 2021.3
- JDK版本: JDK 17(推荐使用 JDK 17 或更高版本,因为 Spring Boot 3.x 系列要求 Java 17,Spring Boot 3.5.4 基于 Spring Framework 6.x 和 Jakarta EE 9,它们都要求至少 JDK 17。)
- Spring Boot版本:3.5.4(于25年7月24日发布)
- Maven版本:3.8.2 (或更高)
- Gradle:(如果使用 Gradle 构建工具的话):推荐使用 Gradle 7.5 或更高版本,确保与 JDK 17 兼容。
- 操作系统:Windows 11
1) 分析启动耗时:用 Actuator 的 startup 把“时间花在哪”揪出来
1.1 先别急着改代码:你得先知道慢在哪一段
Spring Boot 提供了 startup(应用启动)端点,可以拿到启动序列里的各个步骤耗时。它支持:
- GET:获取启动步骤的快照
- POST:从缓冲区“drain”(排空)并返回数据
而这个端点背后依赖的是 BufferingApplicationStartup:它会把启动步骤(steps)缓存在内存里,同时记录时间戳与耗时;缓存满了之后就不再记录新的步骤。
StartupEndpoint 本身就是用 BufferingApplicationStartup 来描述启动时间线的。
1.2 怎么开:最小配置(别把端点开一堆,先开你要的)
Actuator 端点需要“启用 + 暴露”才可用,这是 Boot 文档里反复强调的:端点只有在 enabled & exposed 时才算 available。
一个偏“保守又够用”的配置(示例):
management:
endpoints:
web:
exposure:
include: health,info,startup
endpoint:
startup:
enabled: true
端点开到公网前请务必做访问控制(别把“启动细节”送给别人当情报😅)。端点访问控制/启用策略在 Boot 端点文档里有说明。
1.3 “Wait for startup”到底看什么?
你在 startup 时间线里会看到一堆 step(比如 bean 创建、条件评估、WebServer 初始化等)。做法很简单粗暴但有效:
- 把 startup 数据导出来
- 按耗时排序
- 先处理前 3~5 个最慢 step(80/20 法则)
这里的核心心法:你要优化的是“最慢的那几段”,不是“所有段都微调 5ms”。
2) Lazy Initialization:全局懒加载能救命,但它也会“把问题挪到运行时”😈
2.1 怎么开:spring.main.lazy-initialization=true
Spring Boot 官方文档明确写了:可以用 spring.main.lazy-initialization 开启懒初始化;也可以用 SpringApplication/SpringApplicationBuilder 的 API 方式开启。
spring:
main:
lazy-initialization: true
它的机制是:Bean 不是在启动时就全部实例化,而是“用到再创建”。Spring Framework 文档对 lazy-init 的定义也很直白:第一次被请求时才创建,而不是启动时就预实例化。
2.2 它的副作用:启动变快了,但“首个请求”可能变慢
Spring Boot 参考文档(老版本也明确指出这一点)提到:在 Web 应用里,开启 lazy init 会导致很多 Web 相关 bean 直到收到 HTTP 请求才初始化;缺点是它可能延迟暴露应用问题(启动时没报错,不代表运行时不会炸)。
我用更“人话”翻译一下:
- ✅ 启动速度:通常能明显提升(尤其是 bean 多、自动配置多的项目)
- ⚠️ 首次流量:可能出现“冷启动抖一下”(第一个请求触发一堆 bean 初始化)
- ⚠️ 稳定性:某些配置/依赖问题不再在启动阶段暴露,而是在运行时才暴露(最讨厌这种😵💫)
2.3 更稳的用法:全局懒 + 关键 Bean 显式 eager
Boot 文档也说了:如果全局 lazy 了,但某些 bean 你想让它仍然 eager,可以用 @Lazy(false) 显式关闭该 bean 的懒加载。
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
@Lazy(false) // 我就要你启动时创建
@Component
public class WarmupCriticalBean {
}
我的经验:
- 网关/鉴权/路由表/DB连接池初始化这类“基础设施”,更适合 eager(不然第一个用户就替你踩坑了🥲)
- 业务边角功能、后台任务、低频组件,更适合 lazy
3) 减少 Classpath 扫描范围:@ComponentScan 别扫全宇宙
启动慢的一大类根因就是:组件扫描扫描太广,尤其是把“根包”放得太顶,导致 classpath 里很多无关类都被扫描。
3.1 @ComponentScan 的官方规则:basePackages/basePackageClasses
Spring Framework 文档说明:你可以在 @ComponentScan 里用 basePackages 指定扫描包,或用 basePackageClasses 指定一个/多个“锚点类”来确定扫描范围。
而 @ComponentScan 的 Javadoc 也明确:basePackageClasses() 或 basePackages() 用来定义要扫描的包;如果不指定,会从标注该注解的类所在包开始扫描。
3.2 实战建议:用 basePackageClasses 更不容易写错
因为字符串包名太容易拼错/改包名忘了改配置;用类当锚点就踏实很多。
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
@ComponentScan(basePackageClasses = {
com.myteam.app.Application.class, // 主业务
com.myteam.shared.SharedConfig.class // 共享模块(可选)
})
public class Application {}
小吐槽:我见过有人
@ComponentScan("com")……嗯,启动慢是应该的🙂
3.3 更狠的方式:不扫组件,改成“显式导入”
Spring Boot 文档甚至给了一个思路:你可以让 @Component 和 @ConfigurationProperties 不自动检测,改为显式 @Import 用户自定义 bean(属于“我知道我在干啥”的高级模式)。
这种方式适合:
- 超大项目
- 模块特别多
- 想把启动路径变得极可控(但维护成本会上升)
4) 剔除无用的 AutoConfiguration:别让 Boot 给你“热心办坏事”
Spring Boot 官方文档说得非常明确:如果有你不想要的自动配置类生效,可以用 @SpringBootApplication(exclude=...) 禁用它。
4.1 方式一:@SpringBootApplication(exclude=...)
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
public class Application {}
Boot 文档就是这么示例的。
4.2 方式二:用属性 spring.autoconfigure.exclude
@EnableAutoConfiguration 的 Javadoc 也提到:除了 exclude()/excludeName(),你还可以通过 spring.autoconfigure.exclude 属性来排除自动配置。
spring:
autoconfigure:
exclude:
- org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
4.3 你该“剔除谁”?——不要靠猜,靠 startup/条件评估报告
建议顺序:
- 用
startup端点找耗时大户(谁初始化慢) - 再看它来自哪个 auto-configuration(通常能沿着 bean 定义/自动配置类追到源头)
- 确认你确实不需要它(比如你根本不用 JDBC,却引入了会触发 DataSource 相关自动配置的依赖)
- 再 exclude
这里最容易犯的错:
“我觉得用不到就删” → 结果删掉的是别人依赖的基础设施 → 应用能启动但运行时坏得更隐蔽😇
所以我更推荐“先用 startup 数据排雷,再动刀”。
一套“我会这么干”的落地流程(不玄学版)✅
-
开 Actuator startup,抓一次启动数据(本地/测试环境都行)
-
Top N 排序:先对最慢 3~5 个 step 下手
-
能靠配置解决的先配:
spring.main.lazy-initialization=true(再把关键 bean@Lazy(false)拉回来)
-
扫描范围收紧:
@ComponentScan(basePackageClasses=...)控制边界
-
排除无用自动配置:
exclude或spring.autoconfigure.exclude
-
再抓一次 startup 数据对比(你得看到“哪段变快了”,不然优化没闭环)
🧧福利赠与你🧧
无论你是计算机专业的学生,还是对编程有兴趣的小伙伴,都建议直接毫无顾忌的学习此专栏「滚雪球学SpringBoot」,bug菌郑重承诺,凡是学习此专栏的同学,均能获取到所需的知识和技能,全网最快速入门SpringBoot,就像滚雪球一样,越滚越大, 无边无际,指数级提升。
最后,如果这篇文章对你有所帮助,帮忙给作者来个一键三连,关注、点赞、收藏,您的支持就是我坚持写作最大的动力。
同时欢迎大家关注公众号:「猿圈奇妙屋」 ,以便学习更多同类型的技术文章,免费白嫖最新BAT互联网公司面试题、4000G PDF编程电子书、简历模板、技术文章Markdown文档等海量资料。
ps:本文涉及所有源代码,均已上传至Gitee:
https://gitee.com/bugjun01/SpringBoot-demo开源,供同学们一对一参考 Gitee传送门https://gitee.com/bugjun01/SpringBoot-demo,同时,原创开源不易,欢迎给个star🌟,想体验下被🌟的感jio,非常感谢❗
🫵 Who am I?
我是 bug菌:
- 热活跃于 CSDN:
https://blog.csdn.net/weixin_43970743| 掘金:https://juejin.cn/user/695333581765240| InfoQ:https://www.infoq.cn/profile/4F581734D60B28/publish| 51CTO:https://blog.51cto.com/u_15700751| 华为云:https://bbs.huaweicloud.com/community/usersnew/id_1582617489455371| 阿里云:https://developer.aliyun.com/profile/uolxikq5k3gke| 腾讯云:https://cloud.tencent.com/developer/user/10216480/articles等技术社区; - CSDN 博客之星 Top30、华为云多年度十佳博主&卓越贡献奖、掘金多年度人气作者 Top40;
- 掘金、InfoQ、51CTO 等平台签约及优质作者;
- 全网粉丝累计 30w+。
更多高质量技术内容及成长资料,可查看这个合集入口 👉 点击查看:https://bbs.csdn.net/topics/612438251 👈️
硬核技术公众号 「猿圈奇妙屋」https://bbs.csdn.net/topics/612438251 期待你的加入,一起进阶、一起打怪升级。
- End -
- 点赞
- 收藏
- 关注作者
评论(0)