Spring Boot启动慢?这5个优化点带你起飞

张开发
2026/4/24 0:34:56 15 分钟阅读

分享文章

Spring Boot启动慢?这5个优化点带你起飞
Spring Boot启动慢这5个优化点带你起飞Spring Boot 启动慢的痛点作为一名后端开发日常工作中经常与 Spring Boot 框架打交道。在项目开发初期Spring Boot 凭借其 “约定优于配置” 的理念确实极大地提高了开发效率快速搭建起基础框架。然而随着项目的不断迭代和功能的持续增加一个令人头疼的问题逐渐凸显出来 ——Spring Boot 的启动速度越来越慢。在本地开发环境中每次修改代码后重启项目都需要漫长的等待时间。有时候只是修改了一个小功能想要快速验证效果却不得不花费数分钟等待项目启动完成这严重打断了开发思路降低了开发效率。就好比你在写一篇文章思路正流畅的时候却因为电脑反应慢每次敲几个字都要卡顿一下极大地影响了创作的连贯性。在生产环境中启动慢的问题更是带来了诸多隐患。每次上线新版本较长的启动时间意味着服务不可用的窗口期变长这期间可能会影响用户的正常使用导致业务损失。而且在一些对服务响应速度要求极高的场景下如电商大促期间启动慢可能会成为压垮系统的最后一根稻草引发连锁反应造成严重的后果。曾经参与过一个电商项目在一次促销活动前夕进行系统升级。由于项目中 Spring Boot 应用启动缓慢新服务迟迟无法上线差点错过活动开始时间幸好及时优化才避免了一场危机。这件事让我深刻认识到Spring Boot 启动慢绝不是一个可以忽视的小问题而是关系到开发效率、系统稳定性和业务发展的关键痛点必须要找到有效的解决办法。优化点一减少业务初始化一问题分析在 Spring Boot 应用启动过程中业务初始化操作是耗时的重要环节。以数据库连接为例建立与数据库的连接需要进行网络通信、身份验证以及资源分配等一系列复杂操作。假设我们使用的是 MySQL 数据库在初始化时Spring Boot 会根据配置文件中的信息加载数据库驱动然后尝试与 MySQL 服务器建立 TCP 连接。这个过程中如果网络不稳定或者服务器负载较高连接的建立可能会花费数秒甚至更长时间。再看 Redis 连接同样需要进行网络握手、配置参数协商等步骤。当应用程序需要与 Redis 集群进行交互时初始化过程还包括对集群节点信息的获取和管理这无疑会进一步增加启动的时间开销。除了这些外部资源连接项目中一些复杂的业务逻辑初始化也会带来问题。比如在一个电商系统中启动时可能需要加载大量的商品数据到内存缓存中以提高后续查询的效率。如果商品数据量庞大这个加载过程会占用大量的 CPU 和内存资源导致启动速度大幅下降。二优化方案减少不必要的依赖是优化的关键一步。在项目的依赖管理文件如 Maven 的 pom.xml 或 Gradle 的 build.gradle中仔细检查每个依赖项。有些依赖可能是在开发过程中临时引入用于测试或实验性功能的但在正式环境中不再需要。例如在开发阶段为了方便调试数据库操作可能引入了一个数据库管理工具的依赖但在生产环境中这个依赖并不会被实际使用就可以将其去除。对于一些非关键的初始化逻辑将其改为异步执行能显著加快启动速度。Spring 框架提供了强大的异步支持使用 Async 注解可以轻松实现这一目标。首先在 Spring Boot 的主启动类上添加 EnableAsync 注解开启异步功能。然后在需要异步执行的初始化方法上添加 Async 注解。比如在一个邮件发送服务中启动时可能需要初始化一些邮件模板和配置信息这些操作并不影响应用的核心功能可以将其异步化importorg.springframework.scheduling.annotation.Async;importorg.springframework.stereotype.Service;ServicepublicclassEmailService{AsyncpublicvoidinitEmailConfig(){// 模拟邮件配置初始化操作如加载模板文件、设置邮件服务器地址等try{Thread.sleep(2000);System.out.println(邮件配置初始化完成);}catch(InterruptedExceptione){Thread.currentThread().interrupt();}}}这样在应用启动时initEmailConfig 方法会被提交到一个异步线程池中执行主线程不会被阻塞从而加快了启动速度。优化点二延迟初始化一原理介绍在 Spring Boot 2.2 版本后引入了一个非常实用的属性 ——spring.main.lazy-initialization。这个属性就像是一个聪明的调度员它的作用是延迟所有 Bean 的初始化。在传统的 Spring Boot 启动过程中就好比一场热闹的聚会所有宾客Bean都会在聚会一开始就全部到场不管他们是否马上要参与活动。这就导致了在启动时需要花费大量时间来初始化每一个 Bean即使有些 Bean 在启动后的很长一段时间内都不会被使用。而有了 spring.main.lazy-initialization 属性后情况就大不一样了。它让宾客们Bean不再需要在聚会一开始就全部到场而是等到有需要的时候才过来。这样一来在启动阶段就只需要处理那些真正急需的 Bean大大减少了启动时的工作量从而加快了启动速度。二实际应用使用这个优化点非常简单只需要在 application.yml 文件中添加如下配置spring:main:lazy-initialization:true配置完成后Spring Boot 应用在启动时只会初始化一些必要的基础设施 Bean而其他业务 Bean 会被延迟初始化。当第一次访问某个需要特定 Bean 的接口或功能时对应的 Bean 才会被初始化。需要注意的是由于这种延迟初始化机制首次访问某些接口时可能会因为 Bean 的初始化而导致响应速度变慢就像聚会中临时邀请宾客宾客需要时间赶过来一样。但后续的访问就会恢复正常速度因为 Bean 已经初始化好了。在一个电商项目中开启延迟初始化后启动时间从原来的 10 秒缩短到了 6 秒虽然首次访问商品列表接口时响应时间增加了 1 秒左右但在后续的操作中整体的系统性能并没有受到影响而启动速度的提升却带来了开发和部署效率的显著提高 。优化点三Spring Context Indexer一功能解析从 Spring5 版本开始Spring 框架提供了一个非常实用的功能 ——spring-context-indexer。在大型 Spring Boot 项目中类的数量众多Spring 在启动时进行类扫描的工作量巨大这是导致启动缓慢的一个重要原因。就好比在一个大型图书馆里找一本书如果没有索引你需要一本本去查找效率非常低。spring-context-indexer 的作用就是提前生成 ComponentScan 的扫描索引就像给图书馆的书籍制作了详细的索引目录。在项目编译阶段它会收集所有带有 Spring 模式注解如 Component、Service、Repository 等的类信息并将这些信息记录在一个特定的索引文件中。当 Spring Boot 应用启动执行 ComponentScan 扫描类时不再需要遍历整个类路径去查找这些注解类而是直接读取这个索引文件快速定位到需要的类从而大大提高了扫描速度减少了启动时间。二使用步骤使用 spring-context-indexer 功能非常简单首先需要在项目的依赖管理文件中导入相关依赖。如果使用 Maven在 pom.xml 文件中添加如下依赖dependencygroupIdorg.springframework/groupIdartifactIdspring-context-indexer/artifactIdoptionaltrue/optional/dependency如果使用 Gradle在 build.gradle 文件中添加annotationProcessororg.springframework:spring-context-indexer添加依赖后在 Spring Boot 的启动类上添加 Indexed 注解importorg.springframework.context.annotation.Indexed;importorg.springframework.boot.SpringApplication;importorg.springframework.boot.autoconfigure.SpringBootApplication;IndexedSpringBootApplicationpublicclassYourApplication{publicstaticvoidmain(String[]args){SpringApplication.run(YourApplication.class,args);}}完成上述操作后重新编译项目。在编译打包之后会在项目的 META-INT 目录下生成一个 spring.components 文件。这个文件就是索引文件它记录了项目中所有被 Spring 管理的组件信息。当应用启动时Spring 会优先读取这个文件快速完成组件扫描大大提高启动速度。在一个拥有上千个类的大型电商项目中使用 spring-context-indexer 后启动时间缩短了约 30%效果十分显著 。优化点四关闭 JMX一默认设置在 Spring Boot 2.2.X 版本以下默认情况下JMXJava Management Extensions是开启的。JMX 是 Java 提供的一套标准 API它就像是一个全面的监控室可以用于监控 JVM 状态如内存使用情况、线程活动、垃圾回收GC等还能管理应用组件比如启停服务、调整参数等。通过 JMX我们可以将应用中的组件以 MBeanManaged Bean的形式暴露出来方便进行管理和监控 。二关闭方法如果在项目中不需要使用 JMX 进行监控和管理那么可以手动将其关闭这有助于提升 Spring Boot 应用的启动速度。在 application.yml 文件中添加如下配置即可关闭 JMXspring:jmx:enabled:false关闭 JMX 之所以能提升启动速度是因为在启动过程中Spring Boot 不再需要进行与 JMX 相关的初始化操作如创建 MBeanServer、注册 MBeans 等。这些操作虽然在功能上很强大但对于一些不需要 JMX 功能的项目来说却是额外的负担。就好比一辆汽车原本安装了一些特殊的设备用于专业检测但如果我们只是日常驾驶这些设备不仅占用空间还可能增加启动时的准备时间。关闭 JMX 就像是卸下了这些不必要的设备让 Spring Boot 应用能够轻装上阵更快地完成启动过程 。优化点五关闭分层编译一编译原理从 JDK8 版本开始Java 默认打开了多层编译也就是分层编译机制。这一机制是 JVM 实现 “启动速度” 与 “运行效率” 平衡的核心机制 。在分层编译中涉及到两个重要的编译器Tier3 也就是 C1 编译器以及 Tier4 即 C2 编译器。当 Java 程序开始运行时代码首先会被解释执行。在这个过程中JVM 会监控字节码的执行频率当发现某段代码热点代码被频繁调用时就会触发编译操作。如果方法解释编译达到 2000 次以后就会进行 C1 编译。C1 编译器编译速度快它主要做一些基础的优化如方法内联、常量传播、基本块调度等生成的代码体积小、编译延迟低适合启动阶段或对响应敏感的场景能快速提升代码的执行速度。而当 C1 编译后的代码执行次数达到 15000 次时就会触发 C2 编译。C2 编译器编译速度较慢但它会进行大量深度优化如激进预测、循环优化、锁消除、逃逸分析、循环展开、向量化等能最大化程序的峰值性能生成的代码效率极高非常适合长时间运行的服务端应用 。二优化操作如果我们希望提高 Spring Boot 应用的启动速度可以通过命令使用 C1 编译器同时配合关闭字节码验证的命令。在启动应用时可以添加如下 JVM 参数-XX:TieredStopAtLevel1-XX:-VerifyBytecode其中\-XX:TieredStopAtLevel1表示只使用 C1 编译器停止在 Tier1 层不进行 C2 编译\-XX:\-VerifyBytecode表示关闭字节码验证。字节码验证是 Java 类加载过程中的一个重要环节它会检查字节码是否符合 Java 虚拟机规范是否存在安全隐患等。关闭字节码验证可以节省类加载时间从而加快应用启动速度。不过需要特别强调的是这种优化方式虽然能显著提高启动速度但存在一定风险。关闭字节码验证意味着跳过了对字节码的安全性和合规性检查如果代码中存在错误或安全漏洞可能在运行时才被发现导致应用出现异常甚至崩溃。因此这种优化方式尽量不要在线上环境使用一般只在开发和测试环境中对启动速度有迫切需求且能确保代码质量的情况下采用 。

更多文章