news 2026/5/1 10:53:07

终于解决了!Spring Boot 启动慢的 5 个优化点

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
终于解决了!Spring Boot 启动慢的 5 个优化点

针对 1.4/1.5 老版本,手把手教你把启动时间缩短 50%+

引言:还在等 Spring Boot 启动?可能是你没优化对

很多团队的核心业务系统还在使用 Spring Boot 1.4/1.5 这类早期经典版本:

  • 代码稳定、依赖体系成熟、不敢轻易大版本升级;
  • 但一个启动 30 秒甚至 1 分钟的老项目,严重拖累日常开发调试与部署效率。

实际排查下来,大部分启动慢问题,并不是业务逻辑本身慢,而是:

  • 自动配置加载了一堆用不到的组件;
  • 包扫描范围过大;
  • JVM 参数不合理;
  • 非核心 Bean 提前初始化;
  • 冗余依赖和插件拖后腿。

本文结合老项目实战经验,总结了5 个几乎“零重构”的优化点

  • 不改业务代码、不推翻架构,在原有基础上就能把启动时间轻松缩短 50%+;
  • 特别适用于:老项目维护、开发环境提速、生产部署优化等场景。

一、优化点 1:禁用不必要的自动配置(核心)

1.1 问题原因

在 Spring Boot 1.4/1.5 中,spring-boot-autoconfigure 会根据类路径和配置自动装配大量组件,比如:

  • 数据源(多数据源、JPA、事务)
  • Redis、缓存、消息中间件
  • 邮件发送、模板引擎、Actuator 等

问题在于:只要依赖在类路径上,Spring Boot 就会尝试自动配置 Bean
即使你项目中完全没用到这些功能,也会:

  • 扫描大量配置类;
  • 创建并初始化对应的 Bean;
  • 甚至还会尝试连数据库、拉起连接池。

最终结果:启动阶段 Bean 初始化数量暴增,耗时显著上升

1.2 操作步骤:通过 exclude 精准禁用无用自动配置

步骤 1:打开 debug 日志,查看生效的自动配置

在 application.properties 中增加:

properties

debug=true

启动后控制台会输出类似:

text

========================= AUTO-CONFIGURATION REPORT ========================= Positive matches: DataSourceAutoConfiguration matched ... RedisAutoConfiguration matched ... MailSenderAutoConfiguration matched ... ...

Positive matches 部分就是当前真正生效的自动配置列表
结合业务实际,挑出确定完全不用的配置类。

步骤 2:使用 @SpringBootApplication(exclude = …) 排除

在主启动类上排除这些自动配置,例如:

java

import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration; import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; import org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration; @SpringBootApplication( exclude = { DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class, RedisAutoConfiguration.class, MailSenderAutoConfiguration.class } ) public class LegacyApplication { public static void main(String[] args) { SpringApplication.run(LegacyApplication.class, args); } }

如果你只在某些 profile 下需要这些配置,也可以配合 @ConditionalOnProperty 等方式做更细粒度控制,这里先给出最直接的方式。

1.3 原理说明

Spring Boot 启动过程中的大头工作包括:

  • 扫描 XXXAutoConfiguration 类;
  • 解析条件注解(@ConditionalOnClass、@ConditionalOnMissingBean 等);
  • 注册对应的 BeanDefinition,实例化并注入依赖。

通过 exclude 排除无用自动配置:

  • 这些自动配置类根本不会被处理;
  • 对应的 Bean 不会被创建,不再参与依赖注入与生命周期管理;
  • 启动阶段整体要初始化的 Bean 总数明显减少,从而大幅压缩启动时间。

1.4 注意事项

⚠️ 注意:排除前必须确认业务确实不依赖该组件

如果误排除正在使用的数据源、事务或缓存相关自动配置,可能出现启动失败或运行时异常; 生产环境中建议先在测试环境验证: 启动是否正常; 核心业务回归是否通过。


二、优化点 2:缩小包扫描范围

2.1 问题原因

Spring Boot 默认会:

  • 扫描主类所在包及其子包下的所有类
  • 包括 Controller、Service、Repository、配置类、工具类、甚至某些测试或无用类。

大型老项目中,经常出现这种情况:

  • com.company.project 作为顶级包,下面挂了大量历史包结构;
  • 很多模块早已弃用,但类文件仍然存在;
  • 部分第三方集成 demo 代码也被一起扫描。

结果是:类路径扫描和 Bean 定义解析工作量明显增大,启动开销增加。

2.2 操作步骤:精确指定需要扫描的业务包

步骤 1:使用 scanBasePackages 明确核心业务包

在主启动类中指定扫描范围,例如:

java

import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication( scanBasePackages = { "com.company.project.api", "com.company.project.service", "com.company.project.repository", "com.company.project.config" } ) public class LegacyApplication { public static void main(String[] args) { SpringApplication.run(LegacyApplication.class, args); } }

这样,Spring 只会在上述几个包中查找:

  • @Controller / @RestController
  • @Service / @Component
  • @Repository
  • 以及其他 Spring 管理的 Bean。

步骤 2:对非核心模块使用 @Import 手动引入

如果某些第三方集成、独立模块不想被大范围扫描,可以:

  • 将其配置类从扫描路径中移除;
  • 使用 @Import 在主配置类中显式导入

示例:

java

import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @Configuration @Import({ ThirdPartyDataSourceConfig.class, ExternalJobSchedulerConfig.class }) public class ExtraConfig { }

主启动类仍然只扫描业务核心包,而额外的集成模块通过 @Import 精准引入。

2.3 原理说明

包扫描阶段主要做两件事情:

  • 扫描所有类文件、解析注解元数据;
  • 为符合条件的类注册 BeanDefinition。

缩小扫描范围带来的好处:

  • 扫描的类文件数量大幅减少;
  • 无用的测试类、demo 类不会再参与 Bean 解析;
  • 减少 I/O 和反射操作;
  • 间接减少自动配置和条件匹配的触发次数。

2.4 注意事项

⚠️ 注意:扫描包配置不当会导致 Bean 漏扫

如果某些核心业务 Bean 所在包没被包含进来,会出现启动失败、依赖找不到、Controller 不生效等问题; 调整扫描范围后,建议: 全量跑一次关键接口回归; 对常用 URL 做一次冒烟测试。


三、优化点 3:优化 JVM 启动参数(适配 JDK 8)

3.1 问题原因

Spring Boot 1.4/1.5 多数运行在JDK 8上,如果 JVM 参数不合理,会导致:

  • 内存分配频繁、Full GC 频繁;
  • 类验证、JIT 预热过程缓慢;
  • 导致整体启动时间被 JVM 本身拖慢。

老项目常见问题:

  • 使用默认 JVM 参数;
  • 线上、线下参数混用;
  • 误用了不适配版本的 GC 策略。

3.2 推荐 JVM 参数示例(JDK 8)

开发环境(以启动速度优先)

bash

java \ -Xms512m \ -Xmx512m \ -XX:MetaspaceSize=128m \ -XX:MaxMetaspaceSize=256m \ -XX:+UseCompressedOops \ -XX:+UseG1GC \ -XX:MaxGCPauseMillis=200 \ -XX:+PrintGCDetails \ -XX:+PrintGCDateStamps \ -Xloggc:logs/gc.log \ -Xverify:none \ -Dspring.profiles.active=dev \ -jar legacy-app.jar

关键参数说明:

  • -Xms/-Xmx:固定堆大小,避免频繁扩容;
  • MetaspaceSize/MaxMetaspaceSize:控制类元数据空间,防止过小频繁 GC;
  • UseG1GC:适合大部分服务场景,GC 暂停可控;
  • Xverify:none:关闭类验证,加快类加载(仅推荐开发环境)

生产环境(兼顾稳定性)

bash

java \ -Xms2g \ -Xmx2g \ -XX:MetaspaceSize=256m \ -XX:MaxMetaspaceSize=512m \ -XX:+UseCompressedOops \ -XX:+UseG1GC \ -XX:MaxGCPauseMillis=200 \ -XX:+PrintGCDetails \ -XX:+PrintGCDateStamps \ -Xloggc:/data/logs/gc.log \ -Dspring.profiles.active=prod \ -jar legacy-app.jar

生产环境建议:

  • 不使用 -Xverify:none,保证类验证安全性;
  • 堆大小根据服务负载适当增大,避免 OOM;
  • 保留 GC 日志,便于问题排查。

3.3 原理说明

JVM 启动阶段涉及:

  • 类加载、字节码验证;
  • JIT 编译预热;
  • 堆与元空间初始化。

合理配置 JVM 参数可以:

  • 减少类验证耗时(开发环境);
  • 避免因堆/元空间过小导致的频繁 GC;
  • 提高内存分配与回收效率,间接缩短启动时间。

3.4 注意事项

⚠️ 注意:-Xverify:none 不建议在生产环境使用

关闭类验证会略微提升启动速度,但也可能掩盖某些类加载问题; 生产环境应优先保证稳定性与安全性,仅在开发/测试环境使用此项。


四、优化点 4:延迟初始化非核心 Bean

4.1 问题原因

默认情况下,Spring 在启动时会:

  • 将所有单例 Bean 全部创建并初始化完毕;
  • 包括定时任务、异步服务、缓存预热、报表生成、第三方对接等。

对于很多老项目来说,这些 Bean:

  • 并非每次启动都立即用到;
  • 甚至只在某些功能入口才会首次访问。

但它们却在启动阶段“抢占时间”,导致整体启动明显偏慢。

4.2 操作步骤 1:全局延迟初始化(慎用)

在 application.properties 中配置:

properties

spring.main.lazy-initialization=true

Spring Boot 1.4/1.5 没有这个统一属性,可以通过自定义 BeanFactoryPostProcessor或升级到 2.x 后使用。
若你已做了小版本升级(如维护分支升级到 2.1/2.3),可以直接用此配置。
对于原生 1.4/1.5,可优先考虑“局部延迟初始化”。

4.3 操作步骤 2:局部延迟初始化(推荐)

非核心、非强依赖链上的 Bean使用 @Lazy:

java

import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; @Service @Lazy public class ReportGenerateService { public void generateDailyReport() { // 生成报表的耗时逻辑 } }

也可以在依赖注入处使用:

java

import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; @Service public class OrderService { @Autowired @Lazy private ReportGenerateService reportGenerateService; public void createOrder() { // 下单逻辑 // 只有真正需要生成报表时,才会初始化 ReportGenerateService } }

4.4 原理说明

  • 默认:单例 Bean 在容器刷新阶段一次性创建完毕;
  • 延迟初始化:Bean 第一次被访问时才创建。

将非核心 Bean 延迟初始化,可以:

  • 将原本启动阶段的初始化压力,拆散到运行期间;
  • 明显缩短“应用就绪时间”(从启动命令到可对外提供核心服务的时间);
  • 尤其适合一些“偶发调用”的后台任务、报表、同步 JOB 等。

4.5 注意事项

⚠️ 注意:全局延迟初始化可能隐藏启动期异常

某些 Bean 的配置错误,仅在其首次访问时才会抛出异常; 如果这些 Bean 是核心功能的一部分,可能在系统运行一段时间后才抛出问题; 建议: 优先对明确非核心的 Bean使用 @Lazy; 若使用全局 lazy,务必在测试环境做一次“功能全量走一遍”的回归。


五、优化点 5:移除冗余依赖与插件

5.1 问题原因

老项目演进多年后,典型现象是:

  • POM 中堆满历史依赖:老日志框架、老数据库驱动、废弃组件;
  • 多个 starter 自动带了一堆间接依赖;
  • Maven 插件配置复杂,在打包/启动时执行不必要的任务。

这些冗余依赖和插件会导致:

  • 类路径膨胀、类加载数量暴增;
  • 自动配置命中更多分支;
  • 打包和启动过程执行多余检查或增强步骤。

5.2 操作步骤 1:用 dependency:tree 分析依赖

在项目根目录执行:

bash

mvn dependency:tree > dependency-tree.txt

打开 dependency-tree.txt,重点关注:

  • 未使用的 starter(如 spring-boot-starter-mail、spring-boot-starter-redis 等);
  • 重复版本依赖(多版本 commons-*);
  • 不再使用的中间件 client 包。

对于明确不需要的依赖,可在 pom.xml 中排除,例如:

1)排除内置 Tomcat(改用外置容器时):

xml

<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency> <!-- 使用外部容器时可以去掉内嵌容器相关依赖 --> </dependencies>

2)排除不必要的自动配置模块:

xml

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> <exclusions> <exclusion> <groupId>org.hibernate</groupId> <artifactId>hibernate-entitymanager</artifactId> </exclusion> </exclusions> </dependency>

5.3 操作步骤 2:精简 spring-boot-maven-plugin 配置

典型的插件配置:

xml

<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>1.5.22.RELEASE</version> <configuration> <mainClass>com.company.project.LegacyApplication</mainClass> <!-- 如果不需要,关闭某些额外的 repackage 行为或自定义 layout --> </configuration> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> </plugins> </build>

建议:

  • 若未使用自定义 layout 或不依赖某些高级特性,不要再额外配置多余的 transformer;
  • 构建 pipeline 中避免在每次启动前都做 clean package,而是:开发环境更多使用 IDE 直接运行 main 方法

5.4 原理说明

  • 类路径上的 jar 越多,启动阶段的扫描、类加载、资源查找就越重;
  • 多余的 starter+自动配置会进一步放大自动装配的工作量;
  • 复杂的 Maven 插件在构建阶段做的工作越多,得到可运行包的时间就越长。

清理冗余依赖与插件的本质,就是减少“项目的体重”。

5.5 注意事项

⚠️ 注意:依赖移除/排除前要确认影响范围

使用 mvn dependency:analyze 辅助分析未使用依赖,但结果仅供参考; 移除或排除依赖后,请务必: 重新编译,确保无编译错误; 跑一遍基础集成测试,防止运行时 ClassNotFoundException。


六、优化效果验证:如何量化“到底快了多少”

做完优化,如果没有数据支撑,很难说服团队。这里给出两种简单实用的验证方式。

6.1 在 main 方法中添加启动计时日志

在主启动类中加入简单计时代码:

java

import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class LegacyApplication { public static void main(String[] args) { long start = System.currentTimeMillis(); SpringApplication.run(LegacyApplication.class, args); long end = System.currentTimeMillis(); System.out.println(">>> Application started in " + (end - start) + " ms <<<"); } }

每次启动时,控制台会打印:

text

>>> Application started in 12345 ms <<<

你可以:

  • 记录优化前后的多次启动耗时;
  • 在不同机器/环境下对比,得到一个相对可观的提升比例

6.2 使用 Spring Boot Actuator 分析 Bean 初始化耗时

如果项目中已集成 Actuator,可以启用相关端点。

1)POM 中引入依赖(如尚未添加)

xml

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>

2)在配置中开放必要端点(以 1.5.x 为例)

properties

management.security.enabled=false endpoints.metrics.enabled=true endpoints.beans.enabled=true

然后访问:

  • /metrics:查看应用层面的指标;
  • /beans:查看 Bean 列表,结合日志或监控记录启动阶段 Bean 创建的时间分布。

Tip:在 2.x 中可以借助更多细粒度的指标(如 startup 相关指标)进行可视化分析。


七、总结与拓展

7.1 5 个优化点的实战优先级

在不做大规模重构、不升级大版本的前提下,可以按下面的优先级落地执行:

  1. 禁用不必要的自动配置(收益最高,改动小,立竿见影);
  2. 缩小包扫描范围(控制扫描粒度,防止历史垃圾代码拖累启动);
  3. 延迟初始化非核心 Bean(让启动阶段更“瘦身”,核心功能先跑起来);
  4. JVM 参数优化(中长期收益可观,需结合环境调优);
  5. 移除冗余依赖与插件(治理项目“技术债”,顺带给后续升级打基础)。

配合这 5 个优化点,实践中老项目启动时间从30 秒+ 压缩到 10~15 秒是完全可以做到的。

7.2 拓展建议

  • 逐步升级到 Spring Boot 2.x
    • 新版本在自动配置、启动性能、监控指标等方面都有明显优化;
    • 可考虑先引入兼容性分支,小步升级到 2.1/2.3 等长期维护版本。
  • 引入 Spring Boot DevTools 提升开发体验
    • 支持自动重启、静态资源热加载;
    • 与 IDE 配合使用,可以极大缩短“改代码→看效果”的闭环时间。

⚠️ 建议:老项目的性能治理,要把“启动时间优化”纳入整体技术债清单

与依赖升级、JDK 升级、日志体系重构等一起规划; 避免每次都是临时救火,而是有节奏地演进。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/1 10:00:41

ES在SpringBoot集成使用

1. 在Docker中安装Elasticsearch 这里不了解docker甚至还没安装虚拟机的小伙伴可以去黑马的视频下找到资源&#xff0c;去下一个虚拟机 这里就不对ES进行过多介绍了&#xff0c;具体的效果去b站看看叭&#xff0c;这里直接速成使用 安装ES并启动 docker pull elasticsearch &l…

作者头像 李华
网站建设 2026/5/1 6:49:24

基于微信小程序的新鲜到家生鲜超市销售系统(毕设源码+文档)

课题说明本课题聚焦基于微信小程序的新鲜到家生鲜超市销售系统的设计与实现&#xff0c;核心目标是解决传统生鲜超市销售中线下客流有限、商品展示不全面、订单处理效率低、配送调度混乱、生鲜保鲜追溯难及用户购物体验差等痛点问题。系统深度依托微信小程序生态优势&#xff0…

作者头像 李华
网站建设 2026/5/1 6:00:56

Docker部署Spring Boot + Vue项目

目录 前提条件 概述 下载代码 打开代码 Docker创建网络 MySQL容器准备 MySQL数据库配置 启动MySQL容器 测试连接MySQL 初始化MySQL数据 Redis容器准备 修改Redis配置 启动redis容器 部署后端 后端代码打包 上传jar包到Linux 创建Dockerfile 构建镜像 运行后…

作者头像 李华
网站建设 2026/5/1 7:08:34

docker网络模式及配置

一、Docker网络模式 docker run 创建docker容器时&#xff0c;可以用-net选项指定容器的网络模式&#xff0c;docker有以下4种网络模式&#xff1a; host 模式&#xff0c;使用-nethost指定。container模式&#xff0c;使用-netcontainer:NAME_or_ID指定。none模式&#xff0…

作者头像 李华
网站建设 2026/5/1 7:18:49

基于Python爬虫的网络小说热度分析django-计算机毕业设计源码+LW文档

摘 要 在数字化信息飞速发展的当下&#xff0c;网络文学市场规模不断扩大&#xff0c;网络小说热度分析对于了解读者喜好、优化平台运营具有重要意义。随着互联网技术的持续演进&#xff0c;海量的网络小说数据蕴含着丰富的读者行为和偏好信息&#xff0c;如何从中挖掘有价值的…

作者头像 李华
网站建设 2026/5/1 7:19:01

微信小程序 PHP_uniapp的共享充电桩系统_d40o1910

目录共享充电桩系统概述核心功能模块技术实现特点应用场景与优势项目开发技术介绍PHP核心代码部分展示系统结论源码获取/同行可拿货,招校园代理共享充电桩系统概述 微信小程序 PHP_uniapp共享充电桩系统&#xff08;d40o1910&#xff09;是一个基于微信生态的智能充电服务平台…

作者头像 李华