1.概述
Gradle 6.0版本带来了一些新的功能,这有助于使我们建立更加高效和稳健。这些功能包括改进的依赖性管理,模块元数据发布,避免任务配置以及对JDK 13的支持。
在本文中,将介绍Gradle 6.0中可用的新功能。我们的示例构建文件将使用Gradle的Kotlin DSL。
2.依赖管理的改进
近年来,随着每个版本的发布,Gradle对项目如何管理依赖项进行了逐步改进,这些依赖性改进最终在Gradle 6.0中得以实现,先回顾一下目前稳定的依赖性管理改进。
2.1 API和实现分离
在Java的库,插件可以帮助我们创建一个可重复使用的Java库,该插件鼓励我们将属于库公共API的依赖项与作为实现细节的依赖项分开。这种分离使构建更加稳定,因为用户不会意外引用不属于库公共API的类型。
2.2 丰富的版本
我们的项目依赖关系图通常具有相同依赖关系的多个版本,发生这种情况时,Gradle需要选择项目最终将使用的依赖项版本。
Gradle 6.0允许我们向依赖项添加丰富的版本信息,丰富的版本信息可帮助Gradle在解决依赖项冲突时做出最佳选择。
例如,考虑一个依赖于Guava的项目,进一步假设该项目使用Guava 28.1-jre版本,即使我们知道它仅使用自10.0版本以来一直稳定的Guava API。
我们可以使用需要声明告诉Gradle,这个项目可以使用Guava的任何版本,因为10.0,我们使用喜欢 声明告诉Gradle应该使用28.1的JRE,如果没有其他约束阻止它这样做。在因为声明增加了一个说明,解释这种丰富的版本信息:
<code>implementation("com.google.guava:guava") {
version {
require("10.0")
prefer("28.1-jre")
because("Uses APIs introduced in 10.0. Tested with 28.1-jre")
}
}/<code>
这如何帮助我们的建筑更稳定?假设该项目还依赖于必须使用Guava版本16.0 的依赖项foo。foo项目的构建文件将声明该依赖项为:
<code>dependencies {
implementation("com.google.guava:guava:16.0")
}/<code>
由于foo项目依赖于Guava 16.0,而我们的项目依赖于Guava 28.1-jre和foo,因此存在冲突。Gradle的默认行为是选择最新版本。但是,在这种情况下,选择最新版本是错误的选择,因为foo必须使用版本16.0。
在Gradle 6.0之前,用户必须自行解决冲突。由于Gradle 6.0允许我们告诉Gradle我们的项目可能使用的Guava版本低至10.0,因此Gradle将正确解决此冲突并选择16.0版本。
除了require和Preferred声明之外,我们还可以使用strict和reject声明。在严格的声明描述了一个依赖版本范围,reject 声明则说明与我们的项目不兼容版本的依赖。
如果我们的项目依赖于我们知道将在Guava 29中删除的API,那么我们将使用严格的声明来防止Gradle使用大于28的Guava版本。同样,如果我们知道Guava 27.0中存在一个错误会导致我们项目的问题,我们使用 reject将其排除:
<code>implementation("com.google.guava:guava") {
version {
strictly("[10.0, 28[")
prefer("28.1-jre")
reject("27.0")
because("""
Uses APIs introduced in 10.0 but removed in 29. Tested with 28.1-jre.
Known issues with 27.0
""")
}
}/<code>
2.3 平台类
在Java的平台插件允许我们重用一套跨项目的依赖约束,平台作者声明了一组紧密耦合的依赖关系,这些依赖关系的版本由平台控制。
依赖平台的项目无需为平台控制的任何依赖项指定版本。Maven用户将发现它类似于Maven父POM的dependencyManagement功能。
平台在多项目构建中特别有用,多项目构建中的每个项目都可以使用相同的外部依赖项,我们不希望这些依赖项的版本不同步。
让我们创建一个新平台,以确保我们的多项目构建在各个项目中使用相同版本的Apache HTTP Client。首先,我们创建一个使用java平台插件的项目httpclient-platform:
<code>plugins {
`java-platform`
}/<code>
接下来,我们声明此平台中包含的依赖项的约束。在此示例中,我们将选择我们要在项目中使用的Apache HTTP组件的版本:
<code>dependencies {
constraints {
api("org.apache.httpcomponents:fluent-hc:4.5.10")
api("org.apache.httpcomponents:httpclient:4.5.10")
}
}/<code>
最后,让我们添加一个使用Apache HTTP Client Fluent API 的person-rest-client项目。在这里,我们使用platform方法添加对我们的httpclient-platform项目的依赖。
我们还将添加对org.apache.httpcomponents:fluent-hc的依赖,此依赖项不包含版本,因为httpclient-platform确定要使用的版本:
<code>8
plugins {
`java-library`
}
dependencies {
api(platform(project(":httpclient-platform")))
implementation("org.apache.httpcomponents:fluent-hc")
}/<code>
在Java的平台插件有助于避免在运行时由于构建错位依赖不受欢迎的惊喜。
2.4 测试治具
在Gradle 6.0之前,想要在项目之间共享测试装置的构建作者将这些装置提取到另一个库项目中。现在,构建作者可以使用java-test-fixtures插件从其项目中发布测试装置。
让我们构建一个库,定义一个抽象并发布测试夹具,以验证该抽象期望的合同。
在此示例中,我们的抽象是Fibonacci序列生成器,测试夹具是JUnit 5测试混入。Fibonacci序列生成器的实现者可以使用测试混入来验证他们已正确实现了序列生成器。
首先,让我们为抽象和测试夹具创建一个新项目fibonacci-spi。该项目需要java-library和java-test-fixtures插件:
<code>plugins {
`java-library`
`java-test-fixtures`
}/<code>
接下来,让我们将JUnit 5依赖项添加到我们的测试装置中,就像java-library插件定义api和实现配置一样,java-test-fixtures插件定义testFixturesApi和testFixturesImplementation配置:
<code>dependencies {
testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.5.2")
testFixturesImplementation("org.junit.jupiter:junit-jupiter-engine:5.5.2")
}/<code>
有了我们的依赖关系之后,让我们向由java-test-fixtures插件创建的src / testFixtures / java源集中添加一个JUnit 5测试混入,此测试混入验证了我们的FibonacciSequenceGenerator抽象的约定:
<code>public interface FibonacciSequenceGeneratorFixture {
FibonacciSequenceGenerator provide();
@Test
default void whenSequenceIndexIsNegative_thenThrows() {
FibonacciSequenceGenerator generator = provide();
assertThrows(IllegalArgumentException.class, () -> generator.generate(-1));
}
@Test
default void whenGivenIndex_thenGeneratesFibonacciNumber() {
FibonacciSequenceGenerator generator = provide();
int[] sequence = { 0, 1, 1, 2, 3, 5, 8 };
for (int i = 0; i < sequence.length; i++) {
assertEquals(sequence[i], generator.generate(i));
}
}
}/<code>
这是我们与其他项目共享此测试装置所需要做的一切。
现在,让我们创建一个新项目fibonacci-recursive,它将重用此测试夹具。该项目将在我们的依赖关系块中使用testFixtures方法来声明对我们的fibonacci-spi项目的测试装置的依赖关系:
<code>dependencies {
api(project(":fibonacci-spi"))
testImplementation(testFixtures(project(":fibonacci-spi")))
testImplementation("org.junit.jupiter:junit-jupiter-api:5.5.2")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.5.2")
}/<code>
最后,我们现在可以使用fibonacci-spi项目中定义的测试混入为递归斐波那契序列生成器创建一个新测试:
<code>6
class RecursiveFibonacciUnitTest implements FibonacciSequenceGeneratorFixture {
@Override
public FibonacciSequenceGenerator provide() {
return new RecursiveFibonacci();
}
}/<code>
Gradle 6.0 java-test-fixtures插件为构建作者提供了更大的灵活性,可以在项目之间共享其测试夹具。
3. Gradle模块元数据发布
传统上,Gradle项目将构建工件发布到Ivy或Maven存储库。这包括分别生成ivy.xml或pom.xml元数据文件。
ivy.xml和pom.xml模型无法存储我们在本文中讨论的丰富依赖项信息。这意味着当我们将库发布到Maven或Ivy存储库时,下游项目不会从此丰富的依赖项信息中受益。
Gradle 6.0通过引入Gradle模块元数据规范解决了这一空白。Gradle模块元数据规范是一种JSON格式,支持存储Gradle 6.0中引入的所有增强的模块依赖元数据。
除传统的ivy.xml和pom.xml元数据文件外,项目还可以构建此元数据文件并将其发布到Ivy和Maven存储库。这种向后兼容性使Gradle 6.0项目可以利用此模块元数据(如果存在)而不会破坏传统工具。
要发布Gradle Module元数据文件,项目必须使用新的Maven Publish插件或Ivy Publish插件。从Gradle 6.0开始,这些插件默认情况下会发布Gradle模块元数据文件。这些插件取代了旧版发布系统。
3.1 将Gradle模块元数据发布到Maven
让我们配置一个构建以将Gradle模块元数据发布到Maven。首先,我们在构建文件中包含maven-publish:
<code>plugins {
`java-library`
`maven-publish`
}/<code>
接下来,我们配置发布,出版物可以包含任意数量的工件,让我们添加与java配置关联的工件:
<code>publishing {
publications {
register("mavenJava", MavenPublication::class) {
from(components["java"])
}
}
}/<code>
在Maven的发布插件添加publishToMavenLocal任务,让我们使用以下任务来测试我们的Gradle模块元数据发布:
<code>./gradlew publishToMavenLocal/<code>
接下来,让我们在本地Maven存储库中列出此工件的目录:
<code>ls ~/.m2/repository/com/baeldung/gradle-6/1.0.0/
gradle-6-1.0.0.jar gradle-6-1.0.0.module gradle-6-1.0.0.pom/<code>
正如我们在控制台输出中看到的,Gradle除了生成Maven POM之外,还生成模块元数据文件。
4.避免配置API
从5.1版开始,Gradle鼓励插件开发人员使用新的,孵化式的Configuration躲避API。这些API帮助构建避免可能的较慢的任务配置步骤。Gradle称这种性能改进为“ 避免任务配置”。Gradle 6.0使此孵化API更加稳定。
尽管“避免配置”功能主要影响插件作者,但在其内部版本中创建任何自定义Configuration,Task或Property的内部版本作者也会受到影响。插件作者和构建作者现在都可以使用新的惰性配置API来包装具有Provider类型的对象,以便Gradle避免在需要它们之前“实现”这些对象。
让我们使用惰性API添加自定义任务。首先,我们使用TaskContainer.registering扩展方法注册任务。由于注册返回了TaskProvider,因此Task实例的创建被推迟到Gradle或构建作者调用TaskProvider.get()为止。最后,我们提供一个闭包,将在Gradle创建任务后配置它:
<code>val copyExtraLibs by tasks.registering(Copy::class) {
from(extralibs)
into(extraLibsDir)
}/<code>
Gradle的《避免任务配置迁移指南》可帮助插件作者和构建作者迁移到新的API。对于构建作者而言,最常见的迁移包括:
- task.register而不是task.create
- task.named代替task.getByName
- 配置。注册而不是配置。创建
- project.layout.buildDirectory.dir(“ foo”)而不是File(project.buildDir,“ foo”)
5. JDK 13支持
Gradle 6.0引入了对使用JDK 13构建项目的支持。我们可以通过熟悉的sourceCompatibility和targetCompatibility设置将Java构建配置为使用Java 13 :
<code>./gradlew publishToMavenLocal/<code>
JDK 13的一些最令人激动的语言功能(例如Raw String Literals)仍处于预览状态。让我们在Java构建中配置任务以启用以下预览功能:
<code>tasks.compileJava {
options.compilerArgs.add("--enable-preview")
}
tasks.test {
jvmArgs.add("--enable-preview")
}
tasks.javadoc {
val javadocOptions = options as CoreJavadocOptions
javadocOptions.addStringOption("source", "13")
javadocOptions.addBooleanOption("-enable-preview", true)
}/<code>
六,结论
在本文中,我们讨论了Gradle 6.0中的一些新功能。
我们介绍了增强的依赖性管理,发布Gradle模块元数据,避免任务配置,以及早期采用者如何配置其构建以使用Java 13预览语言功能。
读者福利
分享一份自己整理好的Java学习资料,里面包含了:分布式架构、高可扩展、高性能、高并发、Jvm性能调优、Spring,MyBatis,Nginx源码分析,Redis,ActiveMQ、、Mycat、Netty、Kafka、Mysql、Zookeeper、Tomcat、Docker、Dubbo、Nginx等多个知识点高级进阶干货。
领取方式:关注+转发,私信回复“资料”,即可免费领取学习资料和“梯子”,感谢大家支持。
閱讀更多 java架構北風 的文章