如何提高 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 文本形式的报告。让我们来看看它:

maven profiler

我们可以看到,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