跳过正文
  1. 博客/
  2. 后端/
  3. 框架/

优化Spring单元测试:从90秒到18秒的实战经验

·5 分钟· ·
后端 框架 Java
目录

引言
#

在大型Spring Boot项目中,缓慢的单元测试执行速度常常成为开发效率的瓶颈。以我司项目为例,原本的单元测试套件需要约90秒才能完成,严重影响了开发流程。经过系统性的优化,我们成功将测试时间缩短至18秒,提升了80%的效率。本文将详细介绍这些优化手段及其原理。

优化前的现状分析
#

在优化前,我们的测试套件存在以下问题:

  • 每次测试都启动完整的Spring上下文
  • 加载了所有自动配置,包括测试中不需要的中间件
  • Bean初始化采用饥饿模式,启动时即初始化所有Bean
  • 缺乏针对测试环境的JVM优化

系统化优化方案
#

1. 精确控制测试环境
#

1.1 禁用Web环境

默认的@SpringBootTest会启动完整的Web环境,包括嵌入式Servlet容器。对于不需要Web功能的测试,可以显式禁用:

@SpringBootTest(  
    classes = ApplicationBootstrap.class,  
    webEnvironment = SpringBootTest.WebEnvironment.NONE  
)  

1.2 排除不必要的自动配置

@EnableAutoConfiguration(exclude = {  
    DubboServiceRegistrationNonWebApplicationAutoConfiguration.class  
})  

2. 智能初始化策略
#

2.1 延迟初始化(Lazy Initialization)

Spring Boot 2.2+支持延迟初始化,Bean只在首次使用时才初始化:

spring.main.lazy-initialization=true  

2.2 使用组件索引加速扫描

添加spring-context-indexer依赖生成组件索引:

<dependency>  
    <groupId>org.springframework</groupId>  
    <artifactId>spring-context-indexer</artifactId>  
    <optional>true</optional>  
</dependency>  

3. 中间件与基础设施优化
#

3.1 禁用服务发现

spring.cloud.service-registry.auto-registration.enabled=false  
spring.cloud.discovery.enabled=false  

3.2 关闭不必要的中间件

假如你们的中间件可以通过环境变量关闭,可以添加对应的环境变量在单元测试中关闭

-Dmanagement.endpoints.enabled-by-default=false  
-Dmanagement.endpoints.web.exposure.exclude=*  
-Dmanagement.endpoint.health.enabled=false  
-Dmanagement.endpoint.shutdown.enabled=false  
-Dmanagement.health.redis.enabled=false  
-Dmanagement.health.db.enabled=true  

4. JVM级别优化
#

4.1 简化JIT编译

-XX:TieredStopAtLevel=1  

4.2 其他JVM优化

-noverify  
-Dspring.jmx.enabled=false  

完整优化配置示例
#

@SpringBootTest(  
    classes = ApplicationBootstrap.class,  
    webEnvironment = SpringBootTest.WebEnvironment.NONE  
)  
@EnableAutoConfiguration(exclude = {  
    DubboServiceRegistrationNonWebApplicationAutoConfiguration.class,  
})  
@ActiveProfiles("test")  
public class OptimizedIntegrationTest {  
    // 测试用例...  
}  

对应的application-test.properties:

spring.main.lazy-initialization=true  
spring.cloud.service-registry.auto-registration.enabled=false  
spring.cloud.discovery.enabled=false  
management.endpoints.enabled-by-default=false  
management.endpoints.web.exposure.exclude=*  
management.endpoint.health.enabled=false  
management.endpoint.shutdown.enabled=false  
management.health.redis.enabled=false  
management.health.db.enabled=true  

JVM参数:

-XX:TieredStopAtLevel=1  
-noverify  
-Dspring.jmx.enabled=false  

PS: 默认IDEA运行新的单元测试,其中JVM参数是默认为空的,你可以修改Junit运行模板,或者添加下面maven 插件,将JVM参数和变量都放到 argLine 中

<plugin>  
    <groupId>org.apache.maven.plugins</groupId>  
    <artifactId>maven-surefire-plugin</artifactId>  
    <version>2.22.2</version>  
    <configuration>  
        <argLine>             
  
        </argLine>  
    </configuration>  
</plugin>  

优化效果对比
#

优化措施 优化前耗时 优化后耗时 节省时间
初始状态 90秒 90秒 0秒
1. 禁用Web环境 90秒 82秒 8秒
2. 禁用服务注册与发现 82秒 67秒 15秒
3. 启用延迟初始化 67秒 52秒 15秒
4. 禁用非必要中间件 52秒 40秒 12秒
5. 使用组件索引 40秒 32秒 8秒
6. JVM编译优化 32秒 27秒 5秒
7. 其他微调(升级Junit5等) 27秒 18秒 9秒

深入分析与思考
#

剩余耗时分析
#

优化后仍存在的耗时点:

  • Spring Boot启动:6秒(必要的框架初始化)
  • 配置中心查询:5秒(如Nacos/Apollo)
  • 数据源和中间件:7秒(即使排除自动配置,部分必要连接仍需建立)

后续优化狠活清单

  1. Bean启动整顿计划
    准备给中间件和核心Bean上紧发条!现在项目启动就像春运火车站,那些拖后腿的Bean一个拽一个。打算用IDEA的CPU耗时分析当X光机,把启动顺序编排得像交警查超载——谁占着茅坑不拉屎,谁敢摸鱼就优化谁,让项目启动从堵车变飙车!

  2. 代码基因改造工程
    是时候给函数式代码做变形手术了!把满世界乱窜的Lambda表达式抓回来,关进面向对象的牢房(啊不,是架构)。先给每个模块安排单元测试当入职体检,等把集成测试这个臃肿大团建拆成敏捷小分队,秒级启动就能跟火箭发射倒计时似的10…9…8…

个人踩坑总结:
#

  1. AI辅助实战血泪史
    现在AI就是个爱吹牛的小助理!我之前想靠它优化项目启动速度,结果被坑惨了——给的方案要么是玄学偏方,要么直接报错。最气人的是它编瞎话脸不红心不跳,明明不知道答案还装专家。现在我看到AI给的方案都忍不住怀疑:这货是不是又在忽悠我?最后还是自己啃Spring源码+用IDEA的CPU性能分析工具,像侦探查案一样揪出耗时的代码,才真正解决问题。

  2. AI生存法则
    别被AI的学霸人设骗了!这货现在就是个会说话的参考资料库,关键时刻还得靠真本事。想在行业里站稳脚跟,必须修炼成领域大神——AI可以帮你搬砖,但盖房子的大梁还得你自己扛。

  3. Java真香警告
    最近优化代码悟了:Python是泡面(写起来快吃完就拉倒),Java是老酒(越优化越香)。特别是性能优化时,Java能把机器性能榨出最后一滴油,这种掌控感真的爽!

  4. AI写作防伪指南
    用AI写博客就像开美颜相机——第一眼惊为天人,看久了满屏塑料味!现在看见"综上所述"这种AI八股文就想点右上角,跟闻到食堂菜里的大锅水煮味似的。不过有个神器功能必须夸:文字草图秒变流程图!现在我让AI当排版美工小弟,核心干货自己掌勺。记住啊,AI是给文章打玻尿酸的,不是换头术——该有的知识骨架还得自己长扎实了,咱要的是通俗易懂的真干货,不是朋友圈装X文学!

成长避坑指南
#

  • AI老师现形记
    之前说AI能培养专家是我天真了!真正的老师会承认"这个我不懂",但AI宁可瞎编也不认怂。现在的AI辅助就像驾校的倒车入库练习杆——撤了杆子照样不会停车。想系统成长?老办法最靠谱:找高手带路!要么花钱买大佬经验,要么死磕行业经典。AI能帮你省点查资料的时间,但要是把它当师父…等着学一肚子错误知识吧!

参考资料:

https://heapdump.cn/article/4215905

相关文章

深入理解Java中的synchronized机制
·5 分钟
后端 框架 Java JVM
一、synchronized概述 # synchronized是Java语言中用于控制并发访问的关键字,它是一种排他锁(独占锁)和可重入锁。作为Java内置的同步机制,它能够确保在同一时刻只有一个线程可以访问被保护的代码块或方法。
大话Java精度问题
·7 分钟
后端 框架 Java
背景 # 事情的起因是,正当我悠闲的品尝一杯Java Caffe的时候,突然飞书一个加急信息铺面而来,“小张啊,你快看下,线上有个用户用优惠券少付一分钱“
Bean复制真的那么慢吗
·3 分钟
后端 框架 Java
引言 # 最近在业务代码中经常用到的BeanUtils.copyProperties,有的时候在想,这个东西在Java里面真方便,但是性能怎么样呢,然后找了一篇博文 https://www.cnblogs.com/kancy/p/12089126.html
Stream源码(1):如何实现去重
·3 分钟
后端 框架 Java Stream
本篇博客是在看代码的时候看到使用Java8使用Stream去重的妙用,从而对Java如何使用Stream实现几行代码 完成一个可支持并行化的流式计算程序
Java的char类型到底几个字节
·6 分钟
后端 框架 Java
引言 # 之所以有这个疑问,是上次阅读Java基础书时碰到讲解char类型没有看明白,并且在代码验证过程中错误的理解了代码的意思,导致我对这么个简单问题产生疑惑并且“恶意揣测”Java内部的黑魔法,这里就把我如何走上歪路,并且最终找到“正确”的道路的故事讲出来
秒杀系统的思考
·2 分钟
后端 框架 Java
秒杀系统