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 库插件)提供了一组与主要 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 发布插件 提供的 publishToMavenLocal 任务。

请注意,Gradle 构建不要求您“安装”工件,因为您可以访问更合适的特性,例如 项目间依赖项复合构建。您应该仅将 publishToMavenLocal 用于与 Maven 构建进行交互。

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

deploy

使用 Maven 发布插件 提供的 publish 任务——确保在您的构建使用旧的 Maven 插件(ID:maven)时从该插件切换。这会将您的包发布到所有已配置的发布存储库。还有一些任务允许您在定义多个存储库时发布到单个存储库。

请注意,Maven 发布插件默认情况下不会发布源 JAR 和 Javadoc JAR,但可以在 构建 Java 项目指南 中说明的指南中轻松激活此功能。

执行自动转换

Gradle 的 init 任务 通常用于创建新的框架项目,但您还可以使用它自动将现有的 Maven 构建转换为 Gradle。一旦 在您的系统上安装了 Gradle,您只需运行以下命令

> gradle init

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

你会发现新的 Gradle 构建包含以下内容

  • POM 中指定的所有自定义存储库

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

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

请参阅构建初始化插件章节,了解自动转换功能的完整列表。

需要注意的一件事是,程序集不会自动转换。此附加转换需要一些手动工作。选项包括

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

> gradle build

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

迁移依赖项

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

这两个工具之间的一个显着差异在于它们管理版本冲突的方式。Maven 使用“最接近”匹配算法,而 Gradle 选择最新版本。不过不用担心,你可以根据管理传递依赖项中的说明,对选择哪些版本有很大的控制权。

在以下部分中,我们将向你展示如何迁移 Maven 构建的依赖项管理信息的常见元素。

声明依赖项

Gradle 使用与 Maven 相同的依赖项标识符组件:组 ID、工件 ID 和版本。它还支持分类器。你只需将依赖项的标识符信息替换为 Gradle 的语法,该语法在声明依赖项章节中进行了描述。

例如,考虑这个对 Log4J 的 Maven 风格依赖项

<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 插件的任何项目,而后者仅可用于专门应用 Java 库插件 的项目。

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

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 平台插件。这允许您声明一组依赖项约束,这些约束可以应用于多个项目。您甚至可以将平台发布为 Maven BOM 或使用 Gradle 的元数据格式。有关如何执行此操作的更多信息,请参阅插件页面,特别是有关 使用平台 的部分,以了解如何将平台应用于同一构建中的其他项目。

排除传递依赖项

Maven 构建使用排除项来将不需要的依赖项(或不需要的依赖项版本)排除在依赖项关系图之外。您也可以对 Gradle 执行相同的操作,但这不一定就是正确的做法。Gradle 提供了其他选项,这些选项可能更适合特定情况,因此您确实需要了解为何排除项已到位才能正确迁移它。

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

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

处理可选依赖项

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

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

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

对于第一个场景,Gradle 的行为与 Maven 相同,它只是忽略声明为可选的任何传递依赖项。它们不会被解析,并且如果相同的依赖项在依赖项关系图中的其他位置作为非可选项出现,则它们不会对所选版本产生任何影响。

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

使用物料清单 (BOM)

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

Gradle 可以出于相同目的使用此类 BOM,使用基于 platform()enforcedPlatform() 方法的特殊依赖项语法。您只需以正常方式声明依赖项,但将依赖项标识符包装在适当的方法中,如本示例所示,该示例“导入”Spring Boot 依赖项 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 依赖项 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
    
    ------------------------------------------------------------
    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 配置文件和属性

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

Gradle 具有类似的项目属性系统,尽管它区分了这些属性和系统属性。例如,您可以在

  • 构建脚本中定义属性

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

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

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

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

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

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

以下示例演示了使用名为 buildProfile 的项目属性和配置文件脚本的基本技术,该脚本仅初始化名为 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 使用脚本文件名中的 buildProfile 值应用适当的配置文件脚本
3 打印 message 额外项目属性的值
4 初始化 message 额外项目属性,其值随后可以在主构建脚本中使用

有了此设置,您可以通过传递您正在使用的项目属性的值来激活其中一个配置文件,在本例中为 buildProfile

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

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

需要记住的一件事是,高级条件语句会使构建更难理解和维护,类似于它们使面向对象代码复杂化的方式。配置文件也适用。Gradle 为您提供了许多更好的方法来避免 Maven 经常需要的广泛使用配置文件,例如通过配置彼此是变体的多个任务。请参阅 Maven 发布插件创建的 publishPubNamePublicationToRepoNameRepository 任务。

有关在 Gradle 中使用 Maven 配置文件的更详细讨论,请参阅 此博客文章

过滤资源

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() 运行。

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

源集还为您提供了很大的灵活性,以便您放置集成测试的源文件。您可以轻松地将它们保留在与单元测试相同的目录中,或者更理想的是,将它们保留在单独的源目录中,例如 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 任务的依赖项,其中还包括 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 通过 ant 对象在 Gradle 构建中是一等公民。例如,您可以像这样使用 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 属性和文件集也得到本机支持。要了解更多信息,请参阅 从 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 插件中根本无效。另一方面,由于 Gradle 具有更丰富的构建模型和 API,因此该插件可能比原始 Maven 插件更容易编写。

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

进一步阅读

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

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