Maven-compilation

由于公司Java项目代码量庞大,使用Maven作为构建工具进行编译时,往往会花费较长时间。

进行代码测试时,修改一行代码,可能会引起整个项目重新编译。这是Maven的默认行为,一旦检测到代码变更,便重新编译改变文件对应的模块。但其实那些没有改动的代码,复用以前的编译结果即可。为此,引入了增量编译。

增量编译

增量编译(Incremental Compilation)是指,只重新编译修改的代码文件。

大部分Java开发人员都使用IDEA作为IDEIDEA自带的构建工具默认支持增量编译,官方文档

When you change any class inside the build target and then execute the build action, IntelliJ IDEA performs the incremental build that compiles only the changed classes. IntelliJ IDEA also recursively builds the classes' dependencies.

IDEA还会进行依赖分析,以确定是否需要连带重新编译其他文件。

Maven则默认不开启增量编译,官方文档

to enable/disable incremental compilation feature.

This leads to two different modes depending on the underlying compiler. The default javac compiler does the following:

  • true (default) in this mode the compiler plugin determines whether any JAR files the current module depends on have changed in the current build run; or any source file was added, removed or changed since the last compilation. If this is the case, the compiler plugin recompiles all sources.
  • false (not recommended) this only compiles source files which are newer than their corresponding class files, namely which have changed since the last compilation. This does not recompile other classes which use the changed class, potentially leaving them with references to methods that no longer exist, leading to errors at runtime.

Maven通过设置<useIncrementalcompilation>标签来控制增量编译行为。比较坑的地方是,选项的实际行为不符合常规理解。设置为true时,关闭增量编译,这是默认行为;设置为false时,开启增量编译。

根据官方文档,Maven会比较源文件(.java)和编译文件(.class)的时间戳。如果源文件的修改时间晚于编译文件,意味着上次编译后源文件发生了变动,文件需要重新编译。默认不推荐增量编译是因为Maven不会进行依赖检查,因此如果修改的位置删除了一个函数,而这个函数在别的源文件中被引用,会导致运行时出错。

无文件变动

无论是否开启增量编译,当所有源文件都没有发生改动时,编译并不会使任何文件重新编译。

Maven会有如下提示:

1
[INFO] Nothing to compile - all classes are up to date

但在实际使用时,可能会出现一种情况:明明无文件变动,但导致了项目重新编译。这是因为项目代码中存在空文件。

空文件是指一个.java文件中,没有声明public class,使其并不会生成对应的.class文件。因此在进行时间戳对比时,会提示文件修改,导致项目重新编译。Maven提示如下:

1
[INFO] Changes detected - recompiling the module!

这种情况一般发生在整个文件都被注释的情况。

测试编译和跳过测试

在使用Maven进行clean compile后,运行单元测试,发现IDEA又将项目代码编译了一遍。由于未设置将IDE构建/运行操作委托给Maven,重新编译使用了IDEA原生构建工具。

研究后发现,Mavencompile阶段并不包括测试的编译,这可以从执行mvn clean compile命令后,target文件夹中并不会有test-classes文件夹生成可以看出。

因此在运行时,IDEA没有找到单元测试的.class文件,会再重新编译。

因此如果要编译整个项目,包括测试,需要使用clean test。但test阶段还默认绑定了单元测试运行,会将项目测试代码中带有@Test的方法都执行一遍,并生成测试报告。这也是日常开发不需要的,因此需要设置跳过测试运行。

如果去百度,可以搜到两条有关测试跳过的运行参数。

  • -DskipTests
  • -Dmaven.test.skip=true

这两条都可以跳过测试运行,区别是-Dmaven.test.skip=true还会额外跳过测试阶段,而-DskipTests只是跳过了测试阶段中的测试运行目标。

为了保证Maven编译完后可以直接运行单元测试,应该选择-DskipTests

总结

Maven是最常用的构建工具,为了开启增量编译功能,需要在pom文件中将compile插件的useIncrementalcompilation属性设置为false。

如果希望Maven编译完后可以直接运行单元测试,需要至少使用test而不是compile。如果只希望编译测试代码,但跳过测试运行,需要设置运行参数-DskipTests


Maven-compilation
http://dracoyus.github.io/2023/05/17/Maven-compilation/
作者
DracoYu
发布于
2023年5月17日
许可协议