What’s New in Gradle 6.0 – Gradle 6.0中的新内容

最后修改: 2020年 2月 3日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

The Gradle 6.0 release brings several new features that will help make our builds more efficient and robust. These features include improved dependency management, module metadata publishing, task configuration avoidance, and support for JDK 13.

Gradle6.0 版本带来了一些新的功能,这将有助于使我们的构建更加高效和稳健。这些功能包括改进的依赖性管理、模块元数据发布、任务配置避免以及对JDK 13的支持。

In this tutorial, we’ll introduce the new features available in Gradle 6.0. Our example build files will use Gradle’s Kotlin DSL.

在本教程中,我们将介绍Gradle 6.0中的新功能。我们的实例构建文件将使用Gradle的Kotlin DSL。

2. Dependency Management Improvements

2.依赖性管理的改进

With each release in recent years, Gradle has made incremental improvements to how projects manage dependencies. These dependency improvements culminate in Gradle 6.0. Let’s review dependency management improvements that are now stable.

近年来,随着每个版本的发布,Gradle对项目管理依赖关系的方式进行了渐进式的改进。这些依赖性改进在Gradle 6.0中达到了顶点。让我们回顾一下现在已经稳定的依赖性管理改进。

2.1. API and Implementation Separation

2.1.API和实现的分离

The java-library plugin helps us to create a reusable Java library. The plugin encourages us to separate dependencies that are part of our library’s public API from dependencies that are implementation details. This separation makes builds more stable because users won’t accidentally refer to types that are not part of a library’s public API.

java-library插件帮助我们创建一个可重用的Java库。该插件鼓励我们将属于库的公共 API 的依赖性与属于实现细节的依赖性分开。这种分离使构建更加稳定,因为用户不会意外地引用不属于库的公共 API 的类型。

The java-library plugin and its api and implementation configurations were introduced in Gradle 3.4. While this plugin is not new to Gradle 6.0, the enhanced dependency management capabilities it provides are part of the comprehensive dependency management realized in Gradle 6.0.

java-library插件及其apiimplementation配置是在Gradle 3.4中引入。虽然这个插件在Gradle 6.0中并不新鲜,但它所提供的增强的依赖性管理能力是Gradle 6.0中实现的全面依赖性管理的一部分。

2.2. Rich Versions

2.2 丰富的版本

Our project dependency graphs often have multiple versions of the same dependency. When this happens, Gradle needs to select which version of the dependency the project will ultimately use.

我们的项目依赖关系图经常有同一依赖关系的多个版本。当这种情况发生时,Gradle需要选择项目最终将使用哪个版本的依赖关系。

Gradle 6.0 allows us to add rich version information to our dependencies. Rich version information helps Gradle make the best possible choice when resolving dependency conflicts.

Gradle 6.0 允许我们向我们的依赖关系添加丰富的版本信息丰富的版本信息有助于Gradle在解决依赖性冲突时做出尽可能好的选择。

For example, consider a project that depends on Guava. Suppose further that this project uses Guava version 28.1-jre, even though we know that it only uses Guava APIs that have been stable since version 10.0.

例如,考虑一个依赖Guava的项目。假设这个项目使用了28.1-jre版本的Guava,尽管我们知道它只使用了从10.0版本开始就稳定的Guava APIs。

We can use the require declaration to tell Gradle that this project may use any version of Guava since 10.0, and we use the prefer declaration to tell Gradle that it should use 28.1-jre if no other constraints are preventing it from doing so. The because declaration adds a note explaining this rich version information:

我们可以使用require声明来告诉Gradle这个项目可以使用自10.0以来的任何版本的Guava,并且我们使用prefer声明来告诉Gradle如果没有其他约束条件阻止它使用28.1-jre。because声明添加了一个说明,解释这个丰富的版本信息。

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")
    }
}

How does this help make our builds more stable? Suppose this project also relies on a dependency foo that must use Guava version 16.0. The build file for the foo project would declare that dependency as:

这如何有助于使我们的构建更加稳定?假设这个项目还依赖于一个必须使用Guava 16.0版本的依赖项foofoo项目的构建文件将声明该依赖关系为。

dependencies {
    implementation("com.google.guava:guava:16.0")
}

Since the foo project depends on Guava 16.0, and our project depends on both Guava version 28.1-jre and foo, we have a conflict. Gradle’s default behavior is to pick the latest version. In this case, however, picking the latest version is the wrong choice, because foo must use version 16.0.

由于foo项目依赖于Guava 16.0,而我们的项目同时依赖于Guava 28.1-jre版本和foo,我们有一个冲突。Gradle的默认行为是选择最新的版本。然而,在这种情况下,选择最新版本是错误的选择,因为foo必须使用16.0版本。

Prior to Gradle 6.0, users had to solve conflicts on their own. Because Gradle 6.0 allows us to tell Gradle that our project may use Guava versions as low as 10.0, Gradle will correctly resolve this conflict and choose version 16.0.

在Gradle 6.0之前,用户必须自己解决冲突。因为Gradle 6.0允许我们告诉Gradle我们的项目可以使用低至10.0的Guava版本,Gradle会正确解决这个冲突并选择16.0版本。

In addition to the require and prefer declarations, we can use the strictly and reject declarations. The strictly declaration describes a dependency version range that our project must use. The reject declaration describes dependency versions that are incompatible with our project.

除了requireprefer声明之外,我们还可以使用strictlyreject声明。strictly声明描述了我们项目必须使用的依赖版本范围。reject声明描述了与我们项目不兼容的依赖版本。

If our project relied on an API that we know will be removed in Guava 29, then we use the strictly declaration to prevent Gradle from using a version of Guava greater than 28. Likewise, if we know there is a bug in Guava 27.0 that causes problems for our project, we use reject to exclude it:

如果我们的项目依赖于一个我们知道将在Guava 29中被移除的API,那么我们使用strictly声明来阻止Gradle使用大于28的Guava版本。同样地,如果我们知道Guava 27.0中有一个bug会给我们的项目带来问题,我们就使用reject来排除它。

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
        """)
    }
}

2.3. Platforms

2.3.平台

The java-platform plugin allows us to reuse a set of dependency constraints across projects. A platform author declares a set of tightly coupled dependencies whose versions are controlled by the platform.

java-platform插件允许我们跨项目重用一组依赖约束。一个平台作者声明了一组紧密耦合的依赖关系,其版本由平台控制。

Projects that depend on the platform do not need to specify versions for any of the dependencies controlled by the platform. Maven users will find this similar to a Maven parent POM’s dependencyManagement feature.

依赖平台的项目不需要为平台控制的任何依赖关系指定版本。Maven用户会发现这类似于Maven父POM的dependencyManagement功能。

Platforms are especially useful in multi-project builds. Each project in the multi-project build may use the same external dependencies, and we don’t want the versions of those dependencies to be out of sync.

平台在多项目构建中特别有用。多项目构建中的每个项目都可能使用相同的外部依赖,而我们不希望这些依赖的版本不同步。

Let’s create a new platform to make sure our multi-project build uses the same version of Apache HTTP Client across projects. First, we create a project, httpclient-platform, that uses the java-platform plugin:

让我们创建一个新的平台,以确保我们的多项目构建在各个项目中使用相同版本的Apache HTTP Client。首先,我们创建一个项目,httpclient-platform,使用java-platform插件。

plugins {
    `java-platform`
}

Next, we declare constraints for the dependencies included in this platform. In this example, we’ll pick the versions of the Apache HTTP Components that we want to use in our project:

接下来,我们声明这个平台中所包含的依赖关系的约束条件。在这个例子中,我们将挑选我们想在项目中使用的Apache HTTP组件的版本。

dependencies {
    constraints {
        api("org.apache.httpcomponents:fluent-hc:4.5.10")
        api("org.apache.httpcomponents:httpclient:4.5.10")
    }
}

Finally, let’s add a person-rest-client project that uses the Apache HTTP Client Fluent API. Here, we’re adding a dependency on our httpclient-platform project using the platform method. We’ll also add a dependency on org.apache.httpcomponents:fluent-hc. This dependency does not include a version because the httpclient-platform determines the version to use:

最后,让我们添加一个person-rest-client项目,使用Apache HTTP Client Fluent API。在这里,我们将使用platform方法添加对我们的httpclient-platform项目的依赖。我们还将添加对org.apache.httpcomponents:fluent-hc的依赖。这个依赖关系不包括版本,因为httpclient-platform决定了要使用的版本。

plugins {
    `java-library`
}

dependencies {
    api(platform(project(":httpclient-platform")))
    implementation("org.apache.httpcomponents:fluent-hc")
}

The java-platform plugin helps to avoid unwelcome surprises at runtime due to misaligned dependencies in the build.

java-platform插件有助于避免由于构建中的依赖关系不一致而在运行时出现不受欢迎的意外。

2.4. Test Fixtures

2.4.测试夹具

Prior to Gradle 6.0, build authors who wanted to share test fixtures across projects extracted those fixtures to another library project. Now, build authors can publish test fixtures from their project using the java-test-fixtures plugin.

在Gradle 6.0之前,想要在项目间共享测试夹具的构建者要将这些夹具提取到另一个库项目。现在,构建者可以使用java-test-fixtures插件从他们的项目中发布测试夹具。

Let’s build a library that defines an abstraction and publishes test fixtures that verify the contract expected by that abstraction.

让我们建立一个库,它定义了一个抽象,并发布了测试夹具,以验证该抽象所期望的契约。

In this example, our abstraction is a Fibonacci sequence generator, and the test fixture is a JUnit 5 test mix-in. Implementors of the Fibonacci sequence generator may use the test mix-in to verify they have implemented the sequence generator correctly.

在这个例子中,我们的抽象概念是一个斐波那契数列发生器,而测试夹具是一个JUnit 5测试混入。斐波那契数列发生器的实现者可以使用测试混合器来验证他们是否正确地实现了该数列发生器。

First, let’s create a new project, fibonacci-spi, for our abstraction and test fixtures. This project requires the java-library and java-test-fixtures plugins:

首先,让我们创建一个新项目,fibonacci-spi,用于我们的抽象和测试夹具。这个项目需要java-libraryjava-test-fixtures插件。

plugins {
    `java-library`
    `java-test-fixtures`
}

Next, let’s add JUnit 5 dependencies to our test fixtures. Just as the java-library plugin defines the api and implementation configurations, the java-test-fixtures plugin defines the testFixturesApi and testFixturesImplementation configurations:

接下来,让我们把JUnit 5的依赖性添加到我们的测试固定程序中。正如java-library插件定义了apiimplementation配置,java-test-fixtures插件定义了testFixturesApitestFixturesImplementation配置。

dependencies {
    testFixturesApi("org.junit.jupiter:junit-jupiter-api:5.8.1")
    testFixturesImplementation("org.junit.jupiter:junit-jupiter-engine:5.8.1")
}

With our dependencies in place, let’s add a JUnit 5 test mix-in to the src/testFixtures/java source set created by the java-test-fixtures plugin. This test mix-in verifies the contract of our FibonacciSequenceGenerator abstraction:

有了我们的依赖,让我们在src/testFixtures/java插件创建的java-test-fixtures源集中添加一个JUnit 5测试混合器。这个测试混合器验证了我们的FibonacciSequenceGenerator抽象的契约。

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));
        }
    }
}

This is all we need to do to share this test fixture with other projects.

这是我们需要做的所有事情,以与其他项目共享这个测试夹具

Now, let’s create a new project, fibonacci-recursive, which will reuse this test fixture. This project will declare a dependency on the test fixtures from our fibonacci-spi project using the testFixtures method in our dependencies block:

现在,让我们创建一个新的项目,fibonacci-recursive,它将重复使用这个测试夹具。这个项目将使用dependencies块中的testFixtures方法声明对fibonacci-spi项目的测试夹具的依赖。

dependencies {
    api(project(":fibonacci-spi"))
    
    testImplementation(testFixtures(project(":fibonacci-spi")))
    testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.1")
    testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.1")
}

Finally, we can now use the test mix-in defined in the fibonacci-spi project to create a new test for our recursive fibonacci sequence generator:

最后,我们现在可以使用fibonacci-spi项目中定义的测试混合器,为我们的递归斐波那契序列发生器创建一个新的测试。

class RecursiveFibonacciUnitTest implements FibonacciSequenceGeneratorFixture {
    @Override
    public FibonacciSequenceGenerator provide() {
        return new RecursiveFibonacci();
    }
}

The Gradle 6.0 java-test-fixtures plugin gives build authors more flexibility to share their test fixtures across projects.

Gradle 6.0的java-test-fixtures插件为构建者提供了更大的灵活性,可以在不同的项目中分享他们的测试夹具

3. Gradle Module Metadata Publishing

3.Gradle模块元数据发布

Traditionally, Gradle projects publish build artifacts to Ivy or Maven repositories. This includes generating ivy.xml or pom.xml metadata files respectively.

传统上,Gradle项目会向Ivy或Maven资源库发布构建工件。这包括分别生成ivy.xml或pom.xml元数据文件。

The ivy.xml and pom.xml models cannot store the rich dependency information that we’ve discussed in this article. This means that downstream projects do not benefit from this rich dependency information when we publish our library to a Maven or Ivy repository.

ivy.xml和pom.xml模型不能存储我们在本文中讨论的丰富的依赖性信息。这意味着,当我们将库发布到Maven或Ivy仓库时,下游项目无法从这些丰富的依赖信息中受益。

Gradle 6.0 addresses this gap by introducing the Gradle Module Metadata specification. The Gradle Module Metadata specification is a JSON format that supports storing all the enhanced module dependency metadata introduced in Gradle 6.0.

Gradle 6.0通过引入Gradle Module Metadata规范来解决这一问题。Gradle 模块元数据规范是一种 JSON 格式,支持存储 Gradle 6.0 中引入的所有增强型模块依赖性元数据。

Projects can build and publish this metadata file to Ivy and Maven repositories in addition to traditional ivy.xml and pom.xml metadata files. This backward compatibility allows Gradle 6.0 projects to take advantage of this module metadata if it is present without breaking legacy tools.

除了传统的ivy.xml和pom.xml元数据文件外,项目还可以构建该元数据文件并发布到Ivy和Maven仓库。这种向后兼容允许Gradle 6.0项目利用这种模块元数据(如果有的话)而不破坏传统工具。

To publish the Gradle Module Metadata files, projects must use the new Maven Publish Plugin or Ivy Publish Plugin. As of Gradle 6.0, these plugins publish the Gradle Module Metadata file by default. These plugins replace the legacy publishing system.

要发布Gradle模块元数据文件,项目必须使用新的Maven发布插件Ivy发布插件。从Gradle 6.0开始,这些插件默认会发布Gradle模块元数据文件。这些插件取代了传统的发布系统。

3.1. Publishing Gradle Module Metadata to Maven

3.1.向Maven发布Gradle模块元数据

Let’s configure a build to publish Gradle Module Metadata to Maven. First, we include the maven-publish in our build file:

我们来配置一次构建,将Gradle模块元数据发布到Maven。首先,我们在构建文件中加入maven-publish

plugins {
    `java-library`
    `maven-publish`
}

Next, we configure a publication. A publication can include any number of artifacts. Let’s add the artifact associated with the java configuration:

接下来,我们配置一个出版物。一个出版物可以包括任何数量的工件。让我们添加与java配置相关的工件。

publishing {
    publications {
        register("mavenJava", MavenPublication::class) {
            from(components["java"])
        }
    }
}

The maven-publish plugin adds the publishToMavenLocal task. Let’s use this task to test our Gradle Module Metadata publication:

maven-publish插件增加了publishToMavenLocal任务。让我们用这个任务来测试我们的Gradle模块元数据发布。

./gradlew publishToMavenLocal

Next, let’s list the directory for this artifact in our local Maven repository:

接下来,让我们列出本地Maven资源库中该工件的目录。

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

As we can see in the console output, Gradle generates the Module Metadata file in addition to the Maven POM.

从控制台输出中我们可以看到,除了Maven POM,Gradle还生成了模块元数据文件。

4. Configuration Avoidance API

4.配置规避API

Since version 5.1, Gradle encouraged plugin developers to make use of new, incubating Configuration Avoidance APIs. These APIs help builds avoid relatively slow task configuration steps when possible. Gradle calls this performance improvement Task Configuration Avoidance. Gradle 6.0 promotes this incubating API to stable.

从5.1版本开始,Gradle鼓励插件开发者使用新的、正在孵化的配置避免API。这些API可以帮助构建者尽可能地避免相对较慢的任务配置步骤。Gradle称这种性能改进为任务配置避免。Gradle 6.0将这个孵化中的API提升为稳定版。

While the Configuration Avoidance feature mostly affects plugin authors, build authors who create any custom Configuration, Task, or Property in their build are also affected. Plugin authors and build authors alike can now use the new lazy configuration APIs to wrap objects with the Provider type, so that Gradle will avoid “realizing” these objects until they’re needed.

虽然配置规避功能主要影响到插件作者,但在构建中创建任何自定义ConfigurationTaskProperty的构建作者也会受到影响。插件作者和构建作者现在都可以使用新的lazy configuration APIs来包装具有Provider类型的对象,这样Gradle就可以避免 “意识到 “这些对象,直到它们被需要

Let’s add a custom task using lazy APIs. First, we register the task using the TaskContainer.registering extension method. Since registering returns a TaskProvider, the creation of the Task instance is deferred until Gradle or the build author calls the TaskProvider.get(). Lastly, we provide a closure that will configure our Task after Gradle creates it:

让我们使用懒惰的API添加一个自定义任务。首先,我们使用TaskContainer.registering扩展方法注册任务。由于registering返回一个TaskProvider,所以Task实例的创建被推迟到Gradle或构建者调用TaskProvider.get()。最后,我们提供了一个闭包,它将在Gradle创建我们的Task后对其进行配置。

val copyExtraLibs by tasks.registering(Copy::class) {
    from(extralibs)
    into(extraLibsDir)
}

Gradle’s Task Configuration Avoidance Migration Guide helps plugin authors and build authors migrate to the new APIs. The most common migrations for build authors include:

Gradle的任务配置避免迁移指南帮助插件作者和构建作者迁移到新的API。构建作者最常见的迁移包括。

  • tasks.register instead of tasks.create
  • tasks.named instead of tasks.getByName
  • configurations.register instead of configurations.create
  • project.layout.buildDirectory.dir(“foo”) instead of File(project.buildDir, “foo”)

5. JDK 13 Support

5.支持JDK 13

Gradle 6.0 introduces support for building projects with JDK 13. We can configure our Java build to use Java 13 with the familiar sourceCompatibility and targetCompatibility settings:

Gradle 6.0引入了对使用JDK 13构建项目的支持。我们可以通过熟悉的sourceCompatibilitytargetCompatibility设置,将我们的Java构建配置为使用Java 13。

sourceCompatibility = JavaVersion.VERSION_13
targetCompatibility = JavaVersion.VERSION_13

Some of JDK 13’s most exciting language features, such as Raw String Literals, are still in preview status. Let’s configure the tasks in our Java build to enable these preview features:

JDK 13 最激动人心的一些语言功能,如原始字符串字面意义,仍处于预览状态。让我们来配置我们的Java构建中的任务,以启用这些预览功能。

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)
}

6. Conclusion

6.结语

In this article, we discussed some of the new features in Gradle 6.0.

在这篇文章中,我们讨论了Gradle 6.0中的一些新特性。

We covered enhanced dependency management, publishing Gradle Module Metadata, Task Configuration Avoidance, and how early adopters can configure their builds to use Java 13 preview language features.

我们涵盖了增强的依赖性管理、发布Gradle模块元数据、任务配置避免,以及早期采用者如何配置他们的构建以使用Java 13预览语言功能。

As always, the code for this article is over on GitHub.

一如既往,本文的代码在GitHub上