Apache Maven 是 Java 和其他基于 JVM 的项目的构建工具。将现有的 Maven 构建迁移到 Gradle 是很常见的。

本指南将通过解释这两种工具之间的差异和相似之处,并提供您可以遵循的步骤来简化该过程,从而帮助您进行此类迁移。

转换构建可能令人畏惧,但您不必孤军奋战。如果您遇到困难,可以搜索我们的文档,在我们的社区论坛上发帖,或通过我们的Slack 频道与我们联系。

提出迁移的理由

Gradle 和 Maven 之间的主要区别在于灵活性、性能、用户体验和依赖管理。

有关这些方面的可视化概述,请参见Maven 与 Gradle 功能比较

自 Gradle 3.0 以来,Gradle 在使 Gradle 构建速度更快方面投入了大量资金,例如构建缓存编译避免和改进的增量 Java 编译器等功能。即使不使用构建缓存,Gradle 现在也比 Maven 快 2-10 倍。有关从 Maven 切换到 Gradle 的深入性能比较和业务案例,请访问此处

通用指南

Gradle 和 Maven 对如何构建项目有着根本不同的看法。Gradle 提供了一个灵活且可扩展的构建模型,该模型将实际工作委托给任务图的执行。Maven 使用固定的线性阶段模型,您可以在其中附加目标(执行工作的事务)。这可能会使两者之间的迁移看起来令人生畏,但迁移可能非常容易,因为 Gradle 遵循与 Maven 相同的许多约定,例如标准项目结构,并且其依赖管理的工作方式类似。

在这里,我们为您列出了一系列步骤,这些步骤将有助于促进将任何 Maven 构建迁移到 Gradle

将旧的 Maven 构建和新的 Gradle 构建并排放置。您知道 Maven 构建可以工作,因此您应该保留它,直到您确信 Gradle 构建生成所有相同的工件为止。这也意味着用户可以尝试 Gradle 构建,而无需创建源代码树的新副本。
  1. 为 Maven 构建创建构建扫描.

    构建扫描将使您更容易可视化现有 Maven 构建中发生的事情。对于 Maven 构建,您将能够看到项目结构、正在使用的插件、构建步骤的时间线等等。请妥善保管,以便您可以在转换项目时将其与 Gradle 构建扫描进行比较。

  2. 开发一种机制来验证两个构建是否生成相同的工件。

    这是至关重要的一步,以确保您的部署和测试不会中断。即使是很小的更改,例如 JAR 中清单文件的内容,也可能导致问题。如果您的 Gradle 构建产生与 Maven 构建相同的输出,这将使您对切换充满信心,并使您更容易实施将带来最大收益的更改。

    这并不意味着您需要在每个阶段都验证每个工件,尽管这样做可以帮助您快速确定问题的根源。您应该专注于关键输出,例如最终报告和已发布或部署的工件。

    您将需要考虑 Gradle 生成的构建输出与 Maven 相比的一些固有差异。生成的 POM 将仅包含消费所需的信息,并且它们将在该场景中正确使用 <compile><runtime> 范围。您还可能会看到存档中文件的顺序和类路径中文件的顺序的差异。大多数差异将是微小的,但值得识别它们并验证它们是否可以接受。

  3. 运行自动转换.

    这将创建您需要的所有 Gradle 构建文件,即使对于多模块构建也是如此。对于更简单的 Maven 项目,Gradle 构建将可以立即运行!

  4. 为 Gradle 构建创建构建扫描.

    构建扫描将使您更容易可视化构建中发生的事情。对于 Gradle 构建,您将能够看到项目结构、依赖项(常规的和项目间的)、正在使用的插件以及构建的控制台输出。

    您的构建此时可能会失败,但这没关系;扫描仍将运行。将 Gradle 构建的构建扫描与 Maven 构建的构建扫描进行比较,并继续向下查看此列表以排除故障。

    我们建议您在迁移期间定期生成构建扫描,以帮助您识别和排除问题。如果您愿意,您还可以使用 Gradle 构建扫描来识别提高构建性能的机会。

  5. 验证您的依赖项并修复任何问题.

  6. 配置集成和功能测试.

    许多测试可以通过配置额外的源集来简单地迁移。如果您正在使用第三方库,例如FitNesse,请查看Gradle 插件门户上是否有合适的社区插件可用。

  7. 用 Gradle 等效项替换 Maven 插件。

    对于常用插件,Gradle 通常具有您可以使用的等效插件。您可能还会发现您可以使用内置的 Gradle 功能替换插件。作为最后的手段,您可能需要通过您自己的自定义插件和任务类型重新实现 Maven 插件。

    本章的其余部分更详细地介绍了将构建从 Maven 迁移到 Gradle 的特定方面。

理解构建生命周期

Maven 构建基于构建生命周期的概念,该生命周期由一组固定的阶段组成。这对于迁移到 Gradle 的用户来说可能是一个挑战,因为构建生命周期是一个新概念。尽管理解 Gradle 构建如何融入初始化配置执行阶段的结构非常重要,但 Gradle 提供了一个辅助功能,可以模拟 Maven 的阶段:生命周期任务

此功能允许您通过创建不执行任何操作的任务来定义自己的“生命周期”,这些任务仅依赖于您感兴趣的任务。为了使 Maven 用户更容易过渡到 Gradle,基本插件(由所有 JVM 语言插件(如Java Library 插件)应用)提供了一组生命周期任务,这些任务与主要的 Maven 阶段相对应。

以下是一些主要的 Maven 阶段以及它们映射到的 Gradle 任务的列表

clean

使用基本插件提供的 clean 任务。

compile

使用 Java 插件和其他 JVM 语言插件提供的 classes 任务。这将编译所有语言的所有源文件的所有类,并通过 processResources 任务执行资源过滤

test

使用 Java 插件提供的 test 任务。它运行单元测试,更具体地说,是构成test 源集的测试。

package

使用基本插件提供的 assemble 任务。这将构建项目的适当程序包;例如,Java 库的 JAR 或传统 Java Web 应用程序的 WAR。

verify

使用基本插件提供的 check 任务。这将运行附加到它的所有验证任务,这些任务通常包括单元测试、任何静态分析任务(例如 Checkstyle)等。如果您想包含集成测试,则必须手动配置这些测试

install

使用 Maven Publish 插件提供的 publishToMavenLocal 任务。

请注意,Gradle 构建不需要您“安装”工件,因为您可以访问更合适的功能,例如项目间依赖复合构建。您应该仅使用 publishToMavenLocal 与 Maven 构建互操作。

Gradle 还允许您解析本地 Maven 缓存中的依赖项,如声明仓库部分所述。

deploy

使用 Maven Publish 插件提供的 publish 任务,确保如果您构建正在使用旧的 Maven 插件(ID:maven),则切换到该插件。这将把您的程序包发布到所有配置的发布仓库。还有一些任务允许您发布到单个仓库,即使定义了多个仓库也是如此。

请注意,Maven 发布插件默认情况下不发布 source 和 Javadoc JAR,但这可以轻松激活,如构建 Java 项目指南中所述。

执行自动转换

Gradle 的 init 任务通常用于创建新的骨架项目,但您也可以使用它来自动将现有的 Maven 构建转换为 Gradle。一旦 Gradle 安装在您的系统上,您所要做的就是运行命令

> gradle init

从根项目目录。这包括解析现有的 POM 并生成相应的 Gradle 构建脚本。如果您要迁移多项目构建,Gradle 还会创建一个 settings 脚本。

您会发现新的 Gradle 构建包括以下内容

  • POM 中指定的所有自定义仓库

  • 您的外部依赖项和项目间依赖项

  • 用于构建项目的适当插件(仅限于Maven PublishJavaWar 插件中的一个或多个)

有关自动转换功能的完整列表,请参见Build Init 插件章节

需要记住的一件事是,程序集不会自动转换。此额外的转换将需要一些手动工作。选项包括

如果您的 Maven 构建没有太多插件或自定义步骤,则只需运行

> gradle build

一旦迁移完成。这将自动运行测试并生成所需的工件。

迁移依赖

Gradle 的依赖管理系统比 Maven 更灵活,但它仍然支持相同的仓库、声明的依赖项、范围(Gradle 中的依赖配置)和传递依赖项的概念。实际上,Gradle 与 Maven 兼容的仓库一起工作,这使得迁移您的依赖项变得容易。

两种工具之间的一个显着区别在于它们如何管理版本冲突。Maven 使用“最近”匹配算法,而 Gradle 选择最新的算法。不过,不用担心,您可以完全控制选择哪个版本,如管理传递依赖项中所述。

在以下各节中,我们将向您展示如何迁移 Maven 构建的依赖管理信息的最常见元素。

声明依赖

Gradle 使用与 Maven 相同的依赖项标识符组件:groupId、artifactId 和 version。它还支持 classifiers。您需要做的就是将依赖项的标识符信息替换为 Gradle 的语法,这在声明依赖项章节中进行了描述。

例如,考虑以下 Maven 风格的 Log4J 依赖项

<dependencies>
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.12</version>
    </dependency>
</dependencies>

此依赖项在 Gradle 构建脚本中看起来如下所示

build.gradle.kts
dependencies {
    implementation("log4j:log4j:1.2.12")  (1)
}
build.gradle
dependencies {
    implementation 'log4j:log4j:1.2.12'  (1)
}
1 将 Log4J 的 1.2.12 版本附加到 implementation 配置(范围)

字符串标识符采用 groupIdartifactIdversion 的 Maven 值,尽管 Gradle 将它们称为 groupmoduleversion

上面的示例提出了一个显而易见的问题:什么是 implementation 配置?它是Java 插件提供的标准依赖配置之一,通常用作 Maven 默认 compile 范围的替代品。

Maven 的范围和 Gradle 的标准配置之间的一些差异归结为 Gradle 区分构建模块所需的依赖项和构建依赖于它的模块所需的依赖项。Maven 没有这种区别,因此发布的 POM 通常包含库的使用者实际上不需要的依赖项。

以下是主要的 Maven 依赖项范围以及您应如何处理它们的迁移

compile

Gradle 有两个配置可以代替 compile 范围使用:implementationapi。前者可用于应用 Java 插件的任何项目,而 api 仅可用于专门应用 Java Library 插件的项目。

在大多数情况下,您应该简单地使用 implementation 配置,特别是如果您正在构建应用程序或 Web 应用程序。但是,如果您正在构建库,则可以在关于构建 Java 库的部分中了解哪些依赖项应使用 api 声明。有关 apiimplementation 之间差异的更多信息,请参阅上面链接的 Java Library 插件章节。

runtime

使用 runtimeOnly 配置。

test

Gradle 区分了编译项目测试所需的依赖项和仅运行测试所需的依赖项。

测试编译所需的依赖项应针对 testImplementation 配置声明。仅运行测试所需的依赖项应使用 testRuntimeOnly

provided

使用 compileOnly 配置。

请注意,War 插件添加了 providedCompileprovidedRuntime 依赖配置。这些配置的行为与 compileOnly 略有不同,并且仅确保这些依赖项未打包在 WAR 文件中。但是,依赖项包含在运行时和测试运行时类路径中,因此如果这是您需要的行为,请使用这些配置。

import

import 范围主要在 <dependencyManagement> 块中使用,并且仅适用于仅 POM 发布。请阅读关于使用材料清单的部分,以了解有关如何复制此行为的更多信息。

您还可以指定对仅 POM 发布的常规依赖。在这种情况下,在该 POM 中声明的依赖项被视为构建的正常传递依赖项。

例如,假设您想为测试使用 groovy-all POM。它是一个仅 POM 发布,其中在 <dependencies> 块内列出了自己的依赖项。Gradle 构建中的相应配置如下所示

build.gradle.kts
dependencies {
    testImplementation("org.codehaus.groovy:groovy-all:2.5.4")
}
build.gradle
dependencies {
    testImplementation 'org.codehaus.groovy:groovy-all:2.5.4'
}

结果是 groovy-all POM 中的所有 compileruntime 范围依赖项都将添加到测试运行时类路径,而只有 compile 范围依赖项会添加到测试编译类路径。具有其他范围的依赖项将被忽略。

声明仓库

Gradle 允许您从任何 Maven 兼容或 Ivy 兼容的仓库检索声明的依赖项。与 Maven 不同,它没有默认仓库,因此您必须至少声明一个仓库。为了与您的 Maven 构建具有相同的行为,只需在您的 Gradle 构建中配置Maven Central,如下所示

build.gradle.kts
repositories {
    mavenCentral()
}
build.gradle
repositories {
    mavenCentral()
}

您还可以使用 repositories {} 块来配置自定义仓库,如仓库类型章节中所述。

最后,Gradle 允许您解析本地 Maven 缓存/仓库中的依赖项。这有助于 Gradle 构建与 Maven 构建互操作,但这不应该是您不需要互操作性时使用的技术。如果您想通过文件系统共享发布的工件,请考虑配置带有 file:// URL 的自定义 Maven 仓库

您可能还对 Gradle 自己的依赖缓存感兴趣,它的行为比 Maven 更可靠,并且可以由多个并发 Gradle 进程安全地使用。

控制依赖项版本

传递依赖项的存在意味着您很容易在依赖项图中最终得到同一依赖项的多个版本。默认情况下,Gradle 将选择图中依赖项的最新版本,但这并不总是正确的解决方案。这就是为什么它提供了几种机制来控制解析给定依赖项的哪个版本。

在每个项目的基础上,您可以使用

控制传递依赖项章节中列出了更多专门的选项。

如果您想确保多项目构建中所有项目的版本一致性,类似于 Maven 中 <dependencyManagement> 块的工作方式,则可以使用Java Platform 插件。这允许您声明一组可以应用于多个项目的依赖约束。您甚至可以将平台发布为 Maven BOM 或使用 Gradle 的元数据格式。有关如何执行此操作的更多信息,请参阅插件页面,特别是关于使用平台的部分,以了解如何将平台应用于同一构建中的其他项目。

排除传递依赖项

Maven 构建使用排除项将不需要的依赖项(或不需要的版本的依赖项)排除在依赖项图之外。您可以使用 Gradle 执行相同的操作,但这不一定是正确的操作。Gradle 提供了其他可能更适合给定情况的选项,因此您确实需要了解为什么存在排除项才能正确迁移它。

如果您想出于与版本无关的原因排除依赖项,请查看关于排除传递依赖项的部分。它向您展示了如何将排除项附加到整个配置(通常是最合适的解决方案)或依赖项。您甚至可以轻松地将排除项应用于所有配置。

如果您更有兴趣控制实际解析的依赖项版本,请参阅上一节。

处理可选依赖项

您可能会遇到两种关于可选依赖项的情况

  • 您的一些传递依赖项被声明为可选

  • 您想在项目发布的 POM 中将您的一些直接依赖项声明为可选

对于第一种情况,Gradle 的行为与 Maven 相同,并且只是忽略声明为可选的任何传递依赖项。它们不会被解析,并且如果相同的依赖项在依赖项图中以非可选形式出现在其他位置,则它们对所选版本没有影响。

至于将依赖项发布为可选,Gradle 提供了一个更丰富的模型,称为特性变体,它将允许您声明库提供的“可选特性”。

使用材料清单 (BOM)

Maven 允许您通过在 packaging 类型为 pom 的 POM 文件的 <dependencyManagement> 部分中定义依赖项来共享依赖约束。然后可以将这种特殊类型的 POM (BOM) 导入到其他 POM 中,以便您在项目中具有一致的库版本。

Gradle 可以使用类似的 BOM,目的相同,它使用基于 platform()enforcedPlatform() 方法的特殊依赖语法。您只需以通常方式声明依赖,但将依赖标识符包装在适当的方法中,如下例所示,该示例“导入”了 Spring Boot Dependencies BOM

build.gradle.kts
dependencies {
    implementation(platform("org.springframework.boot:spring-boot-dependencies:1.5.8.RELEASE"))  (1)

    implementation("com.google.code.gson:gson")  (2)
    implementation("dom4j:dom4j")
}
build.gradle
dependencies {
    implementation platform('org.springframework.boot:spring-boot-dependencies:1.5.8.RELEASE') (1)

    implementation 'com.google.code.gson:gson' (2)
    implementation 'dom4j:dom4j'
}
1 应用 Spring Boot Dependencies BOM
2 添加一个版本由该 BOM 定义的依赖

您可以在关于 从 Maven BOM 导入版本建议 的章节中了解有关此功能以及 platform()enforcedPlatform() 之间差异的更多信息。

您可以使用此功能将任何依赖项的 POM 中的 <dependencyManagement> 信息应用于 Gradle 构建,即使那些依赖项的打包类型不是 pom 也可以。platform()enforcedPlatform() 都将忽略在 <dependencies> 块中声明的任何依赖项。

迁移多模块构建(项目聚合)

Maven 的多模块构建可以很好地映射到 Gradle 的 多项目构建。尝试相应的 示例,了解如何设置基本的 Gradle 多项目构建。

要迁移多模块 Maven 构建,只需按照以下步骤操作

  1. 创建一个设置脚本,使其与根 POM 的 <modules> 块匹配。

    例如,以下 <modules>

    <modules>
        <module>simple-weather</module>
        <module>simple-webapp</module>
    </modules>

    可以通过将以下行添加到设置脚本来迁移

    settings.gradle.kts
    rootProject.name = "simple-multi-module"  (1)
    
    include("simple-weather", "simple-webapp")  (2)
    settings.gradle
    rootProject.name = 'simple-multi-module'  (1)
    
    include 'simple-weather', 'simple-webapp'  (2)
    1 设置整体项目的名称
    2 配置两个子项目作为此构建的一部分
    gradle projects 的输出
    > gradle projects
    
    Projects:
    
    ------------------------------------------------------------
    Root project 'simple-multi-module'
    ------------------------------------------------------------
    
    Root project 'simple-multi-module'
    +--- Project ':simple-weather'
    \--- Project ':simple-webapp'
    
    To see a list of the tasks of a project, run gradle <project-path>:tasks
    For example, try running gradle :simple-weather:tasks
  2. 项目依赖 替换跨模块依赖。

  3. 使用 约定插件 复制项目继承。

    这基本上涉及创建一个根项目构建脚本,将共享配置注入到适当的子项目中。

跨项目共享版本

如果您想复制 Maven 模式,在根 POM 文件的 dependencyManagement 部分声明依赖版本,最好的方法是利用 java-platform 插件。您将需要为此添加一个专用项目,并在构建的常规项目中使用它。有关此模式的更多详细信息,请参阅 文档

迁移 Maven profiles 和 properties

Maven 允许您使用各种属性参数化构建。有些是项目模型的只读属性,另一些是在 POM 中用户定义的。它甚至允许您将系统属性视为项目属性。

Gradle 具有类似的项目属性系统,尽管它区分了项目属性和系统属性。例如,您可以在以下位置定义属性

  • 构建脚本

  • 根项目目录中的 gradle.properties 文件

  • $HOME/.gradle 目录中的 gradle.properties 文件

这些不是唯一的选项,因此,如果您有兴趣了解有关如何以及在何处定义属性的更多信息,请查看 构建环境 章节。

您需要注意的一个重要行为是,当同一个属性在构建脚本和外部属性文件中都定义时会发生什么:构建脚本值始终优先。幸运的是,您可以模仿 profiles 的概念来提供可覆盖的默认值。

这使我们想到了 Maven profiles。这些是根据环境、目标平台或任何其他类似因素启用和禁用不同配置的一种方式。从逻辑上讲,它们只不过是有限的 if 语句。由于 Gradle 有更强大的方式来声明条件,因此它不需要对 profiles 进行正式支持(除非在依赖项的 POM 中)。您可以通过将条件与辅助构建脚本相结合,轻松获得相同的行为,您将会看到。

假设您有不同的部署设置,具体取决于环境:本地开发(默认)、测试环境和生产环境。要添加类似 profile 的行为,您首先在项目根目录中为每个环境创建构建脚本:profile-default.gradleprofile-test.gradleprofile-prod.gradle。然后,您可以根据您自己选择的 项目属性 有条件地应用其中一个 profile 脚本。

以下示例演示了使用名为 buildProfile 的项目属性和 profile 脚本的基本技术,这些脚本只是初始化一个名为 message额外项目属性

build.gradle.kts
val buildProfile: String? by project  (1)

apply(from = "profile-${buildProfile ?: "default"}.gradle.kts")  (2)

tasks.register("greeting") {
    // Store the message into a variable, because referencing extras from the task action
    // is not compatible with the configuration cache.
    val message = project.extra["message"]
    doLast {
        println(message)  (3)
    }
}
profile-default.gradle.kts
val message by extra("foobar")  (4)
profile-test.gradle.kts
val message by extra("testing 1 2 3")  (4)
profile-prod.gradle.kts
val message by extra("Hello, world!")  (4)
build.gradle
if (!hasProperty('buildProfile')) ext.buildProfile = 'default'  (1)

apply from: "profile-${buildProfile}.gradle"  (2)

tasks.register('greeting') {
    // Store the message into a variable, because referencing extras from the task action
    // is not compatible with the configuration cache.
    def message = project.message
    doLast {
        println message  (3)
    }
}
profile-default.gradle
ext.message = 'foobar'  (4)
profile-test.gradle
ext.message = 'testing 1 2 3'  (4)
profile-prod.gradle
ext.message = 'Hello, world!'  (4)
1 检查 (Groovy) 或绑定 (Kotlin) buildProfile 项目属性是否存在
2 应用适当的 profile 脚本,在脚本文件名中使用 buildProfile 的值
3 打印出 message 额外项目属性的值
4 初始化 message 额外项目属性,其值随后可以在主构建脚本中使用

完成此设置后,您可以通过为您正在使用的项目属性传递一个值来激活其中一个 profile — 在本例中为 buildProfile

gradle greeting 的输出
> gradle greeting
foobar
gradle -PbuildProfile=test greeting 的输出
> gradle -PbuildProfile=test greeting
testing 1 2 3

您不限于检查项目属性。您还可以检查环境变量、JDK 版本、构建运行所在的操作系统或您可以想象的任何其他内容。

请记住,高级条件语句会使构建更难以理解和维护,类似于它们使面向对象的代码复杂化的方式。这同样适用于 profiles。Gradle 提供了许多更好的方法来避免 Maven 经常需要大量使用的 profiles,例如,通过配置多个彼此变体的任务。请参阅由 Maven Publish Plugin 创建的 publishPubNamePublicationToRepoNameRepository 任务。

有关在 Gradle 中使用 Maven profiles 的更详细讨论,请查看 此博客文章

过滤资源

Maven 有一个名为 process-resources 的阶段,默认情况下,目标 resources:resources 绑定到该阶段。这使构建作者有机会对各种文件(例如 Web 资源、打包的属性文件等)执行变量替换。

Gradle 的 Java 插件提供了一个 processResources 任务来执行相同的操作。这是一个 ProcessResources 任务,它将文件从配置的资源目录(默认情况下为 src/main/resources)复制到输出目录。与任何 ProcessResourcesCopy 任务一样,您可以配置它来执行 文件过滤重命名内容过滤

例如,这是一个将源文件视为 Groovy SimpleTemplateEngine 模板的配置,为这些模板提供 versionbuildNumber 属性

build.gradle.kts
tasks {
    processResources {
        expand("version" to version, "buildNumber" to currentBuildNumber)
    }
}
build.gradle
processResources {
    expand(version: version, buildNumber: currentBuildNumber)
}

请参阅 CopySpec 的 API 文档,以查看所有可用的选项。

配置集成测试

许多 Maven 构建都包含某种集成测试,Maven 通过一组额外的阶段来支持这些测试:pre-integration-testintegration-testpost-integration-testverify。它还使用 Failsafe 插件代替 Surefire,这样失败的集成测试不会自动使构建失败(因为您可能需要清理资源,例如正在运行的应用程序服务器)。

使用源集可以在 Gradle 中轻松复制此行为,正如我们在关于 Java 和 JVM 项目中的测试 的章节中所解释的那样。然后,您可以配置一个清理任务(例如,关闭测试服务器的任务),以始终在集成测试之后运行,无论它们成功还是失败,使用 Task.finalizedBy()

如果您真的不希望集成测试使构建失败,那么您可以使用 Java 测试章节的 测试执行 部分中描述的 Test.ignoreFailures 设置。

源集还为您提供了很大的灵活性,可以在其中放置集成测试的源文件。您可以轻松地将它们与单元测试放在同一目录中,或者更可取地,放在单独的源目录中,例如 src/integTest/java。为了支持其他类型的测试,只需添加更多源集和 Test 任务。

迁移常用插件

Maven 和 Gradle 共享一种通过插件扩展构建的通用方法。虽然插件系统在表面之下非常不同,但它们共享许多基于功能的插件,例如

  • Shade/Shadow

  • Jetty

  • Checkstyle

  • JaCoCo

  • AntRun(请参阅下文)

为什么这很重要?因为许多插件依赖于标准的 Java 约定,所以迁移只是复制 Gradle 中 Maven 插件的配置的问题。例如,这是一个简单的 Maven Checkstyle 插件配置

...
<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-checkstyle-plugin</artifactId>
  <version>2.17</version>
  <executions>
    <execution>
      <id>validate</id>
      <phase>validate</phase>
      <configuration>
        <configLocation>checkstyle.xml</configLocation>
        <encoding>UTF-8</encoding>
        <consoleOutput>true</consoleOutput>
        <failsOnError>true</failsOnError>
        <linkXRef>false</linkXRef>
      </configuration>
      <goals>
        <goal>check</goal>
      </goals>
    </execution>
  </executions>
</plugin>
...

迁移到 Gradle 时,可以安全地忽略配置块之外的所有内容。在这种情况下,相应的 Gradle 配置如下所示

build.gradle.kts
checkstyle {
    config = resources.text.fromFile("checkstyle.xml", "UTF-8")
    isShowViolations = true
    isIgnoreFailures = false
}
build.gradle
checkstyle {
    config = resources.text.fromFile('checkstyle.xml', 'UTF-8')
    showViolations = true
    ignoreFailures = false
}

Checkstyle 任务会自动添加为 check 任务的依赖项,check 任务也包括 test。如果您想确保 Checkstyle 在测试之前运行,那么只需使用 mustRunAfter(…​) 方法指定顺序即可

build.gradle.kts
tasks {
    test {
        mustRunAfter(checkstyleMain, checkstyleTest)
    }
}
build.gradle
test.mustRunAfter checkstyleMain, checkstyleTest

如您所见,Gradle 配置通常比 Maven 等效配置短得多。您还拥有更灵活的执行模型,因为您不再受 Maven 固定阶段的约束。

在从 Maven 迁移项目时,不要忘记源集。与 Maven 可以提供的相比,这些通常为处理集成测试或生成的源提供了更优雅的解决方案,因此您应该将它们纳入您的迁移计划。

Ant 目标

许多 Maven 构建依赖于 AntRun 插件来自定义构建,而无需实现自定义 Maven 插件的开销。Gradle 没有等效的插件,因为 Ant 是 Gradle 构建中的一等公民,通过 ant 对象。例如,您可以像这样使用 Ant 的 Echo 任务

build.gradle.kts
tasks.register("sayHello") {
    doLast {
        ant.withGroovyBuilder {
            "echo"("message" to "Hello!")
        }
    }
}
build.gradle
tasks.register('sayHello') {
    doLast {
        ant.echo message: 'Hello!'
    }
}

即使是 Ant 属性和 filesets 也受到原生支持。要了解更多信息,请参阅 从 Gradle 使用 Ant

更简单和更清晰的方法可能是直接 创建自定义任务类型 来替换 Ant 为您完成的工作。然后,您可以更容易地从 增量构建 和其他有用的 Gradle 功能中受益。

了解您不需要哪些插件

值得记住的是,Gradle 构建通常比 Maven 构建更易于扩展和自定义。在这种情况下,这意味着您可能不需要 Gradle 插件来替换 Maven 插件。例如,Maven Enforcer 插件允许您控制依赖项版本和环境因素,但这些事情可以在普通的 Gradle 构建脚本中轻松配置。

处理不常用和自定义的插件

您可能会遇到在 Gradle 中没有对应项的 Maven 插件,特别是如果您或您组织中的某人编写了自定义插件。这种情况依赖于您理解 Gradle(以及可能 Maven)的工作原理,因为您通常必须编写自己的插件。

出于迁移的目的,Maven 插件主要有两种类型

  • 使用 Maven 项目对象的插件。

  • 不使用 Maven 项目对象的插件。

为什么这很重要?因为如果您使用后者之一,您可以将其轻松地重新实现为 自定义 Gradle 任务类型。只需定义与 mojo 参数对应的任务输入和输出,并将执行逻辑转换为任务操作即可。

如果插件依赖于 Maven 项目,那么您将不得不重写它。不要从考虑 Maven 插件如何工作开始,而是查看它试图解决什么问题。然后尝试找出如何在 Gradle 中解决该问题。您可能会发现,这两种构建模型差异很大,以至于“转录”Maven 插件代码到 Gradle 插件中根本不会有效。从好的方面来说,插件可能比原始 Maven 插件更容易编写,因为 Gradle 具有更丰富的构建模型和 API。

如果您确实需要实现自定义逻辑,无论是通过构建脚本还是插件,请查看与 插件开发相关的指南。另请务必熟悉 Gradle 的 Groovy DSL 参考,其中提供了您将要使用的 API 的全面文档。它详细介绍了标准配置块(以及支持它们的对象)、系统中的核心类型(ProjectTask 等)以及标准任务类型集。主要入口点是 Project 接口,因为它是支持构建脚本的顶级对象。

进一步阅读

本章涵盖了特定于将 Maven 构建迁移到 Gradle 的主要主题。剩下的只是迁移期间或之后可能有用的其他几个领域

最后,本指南仅触及了 Gradle 的一些功能,我们鼓励您从用户手册的其他章节和我们的 分步示例 中了解其余功能。