如何提高 Maven 的构建速度?
1、概览
在本教程中,我们将学习如何加快 Maven 的构建速度。我们将介绍各种优化构建时间的技术,并评述其优缺点。
2、常规建议
使用正确的 maven phase(阶段) 可以为我们节省大量时间。如果只需要编译代码,就没必要运行完整的 install,并且这会污染我们的本地仓库。
在多模块项目中,可以只重建已更改的模块和依赖于这些模块的模块。例如,如果我们只修改了 module1 和 module2,我们可以运行:
$ mvn clean install -pl module1,module2 -am
3、使用多线程
默认情况下,Maven 构建在单线程中顺序运行。不过,如今所有电脑都有多核。让我们利用这一点,使用 -T
选项并行构建我们的模块:
$ mvn clean install -T 1C
-T 1C
表示 Maven 将在每个可用内核上使用一个线程。-T 4
会强制 Maven 使用四个线程。-T auto
会让 Maven 决定使用的线程数。
最后但并非最不重要的一点是,Maven Reactor 可确保所有相互依赖的模块按顺序运行。
4、测试优化
测试是软件开发的重要组成部分。然而,运行测试需要花费大量时间。
4.1、并行运行测试
默认情况下,Surefire 插件会按顺序运行单元测试。不过,我们可以将其配置为并行运行。例如,要并行运行所有测试套件,并在每个可用内核上使用一个线程,我们可以运行:
mvn clean install -Dparallel=all -DperCoreThreadCount=true
但是,如果我们的项目中没有大量的单元测试,并行化的开销可能会导致更低的效率。
4.2、跳过测试执行
有时,我们并不需要在本地环境中运行测试。Maven -DskipTests
选项会跳过测试的执行,但仍会编译 test 文件夹:
$ mvn clean install -DskipTests
在高度测试的项目中,当我们不需要测试时,跳过测试可以节省我们的时间!
4.3、跳过测试编译
此外,我们还可以使用 -Dmaven.test.skip=true
选项跳过 test execution,甚至不编译它们:
$ mvn clean install -Dmaven.test.skip=true
这种方法可以进一步缩短总的构建时间。
5、优化 JVM 参数
默认情况下,HotSpot JVM 使用分层编译:它同时使用 client 和 server 编译器来优化 Java 字节码。这种功能可优化服务器上的长期进程。然而,编译是一个连续的短期进程。因此,让我们使用以下 JVM 参数:
-XX:-TieredCompilation
: JVM 不会使用分层编译。-XX:TieredStopAtLevel=1
: JVM 只使用 client 编译器。
我们可以创建包含以下内容的 .mvn/jvm.config
文件,让 Maven 使用这些选项:
-XX:-TieredCompilation -XX:TieredStopAtLevel=1
实际上,Maven 使用缓存和增量构建技术来避免重新编译未更改的代码。因此,Maven 需要编译的新代码越多,这种技术的效率就越高。
6、离线
在构建过程中,Maven 会多次往服务器发起请求,例如进行依赖关系解析和插件下载。特别是,它每次都会检查新的 snapshot 更新。我们可以通过进入离线模式(offline mode)来避免这种情况:
$ mvn clean install -o
显然,当我们需要更新某些依赖项时,我们不能使用离线模式。
7、确定瓶颈
如果前面的技巧不能帮助我们将构建时间缩短到可以接受的程度,我们可以借助 profiler 来排除构建过程中的故障。首先,让我们创建一个新的 Maven 项目。然后,按照 Maven Profiler 项目 GitHub 上的说明进行安装。最后,我们需要在 pom.xml
中添加依赖:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-profiler-plugin</artifactId>
<version>1.7</version>
</plugin>
</plugins>
</build>
现在,我们可以使用该命令来更深入地了解我们的构建时间:
$ mvn clean install -Dprofile
构建完成后,我们可以在 .profiler
文件夹中找到 HTML 文本形式的报告。让我们来看看它:
我们可以看到,profiler 列出了所有插件的执行情况,并记录了它们所耗费的时间。第二部分列出了下载的 artifact(工件)。这些信息可以帮助我们识别那些运行时间很长,但在目标环境中几乎没有任何价值的插件。
8、使用 Maven Profiles
一旦发现瓶颈,我们就可以使用 Maven profiles 按需跳过它们,从而节省时间。例如,执行集成测试通常需要很长时间。此外,每次都在本地环境中运行也没有用。
让我们在 pom.xml
中添加 failsafe 插件配置:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>3.1.2</version>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
现在,我们可以添加一个跳过它们的 profile:
<profiles>
<profile>
<id>skipITs</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
由于重写了配置,激活了 skipITs
profile,集成测试现在被跳过了:
$ mvn clean install -PskipITs
9、 使用 Maven Daemon
Maven daemon(守护进程)旨在提高 Maven 的编译速度。守护进程是一个长期存在的后台进程,即使在构建完成后仍处于活动状态。它通过将重要组件保留在内存中来减少构建开销,避免重复启动和初始化过程,从而加快项目构建速度。要安装它,我们可以按照该项目的 GitHub 页面上的说明进行。现在,让我们用它来启动构建:
$ mvnd clean install
如果守护进程需要启动,则构建时间会稍长。最后,我们可以将前面的技术与 Maven 守护进程结合起来。例如,我们可以通过守护进程进行构建,而跳过测试:
$ mvnd clean install -Dmaven.test.skip=true
尽管如此,我们还是要指出,由于 Maven 守护进程是一个长期存在的进程,按照我们之前的方法设置 JVM 参数可能会弊大于利。
10、总结
在本文中,我们展示了使用 Maven 缩短构建时间的各种技术。总之,推荐使用 Maven daemon。然后,记住在不需要运行测试时跳过测试。我们还可以对构建进行配置,使用 Maven profiles 排除耗时的低价值任务。如果这些都还不够,还可以尝试文章中介绍的其他技术。
参考:https://www.baeldung.com/maven-fast-build