Maven-compilation
由于公司Java
项目代码量庞大,使用Maven
作为构建工具进行编译时,往往会花费较长时间。
进行代码测试时,修改一行代码,可能会引起整个项目重新编译。这是Maven
的默认行为,一旦检测到代码变更,便重新编译改变文件对应的模块。但其实那些没有改动的代码,复用以前的编译结果即可。为此,引入了增量编译。
增量编译
增量编译(Incremental Compilation)是指,只重新编译修改的代码文件。
大部分Java
开发人员都使用IDEA
作为IDE
。IDEA
自带的构建工具默认支持增量编译,官方文档。
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 |
|
但在实际使用时,可能会出现一种情况:明明无文件变动,但导致了项目重新编译。这是因为项目代码中存在空文件。
空文件是指一个.java
文件中,没有声明public class
,使其并不会生成对应的.class
文件。因此在进行时间戳对比时,会提示文件修改,导致项目重新编译。Maven提示如下:
1 |
|
这种情况一般发生在整个文件都被注释的情况。
测试编译和跳过测试
在使用Maven
进行clean compile
后,运行单元测试,发现IDEA又将项目代码编译了一遍。由于未设置将IDE构建/运行操作委托给Maven,重新编译使用了IDEA
原生构建工具。
研究后发现,Maven
的compile
阶段并不包括测试的编译,这可以从执行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
。