Gradle 附带了一套强大的核心系统,例如依赖管理、任务执行和项目配置。但它所能做的其他一切都由插件提供。

插件封装了特定任务或集成的逻辑,例如编译代码、运行测试或部署构件。通过应用插件,用户可以轻松地为其构建过程添加新功能,而无需从头编写复杂的代码。

这种基于插件的方法使 Gradle 保持轻量和模块化。它还促进了代码重用和可维护性,因为插件可以在项目之间或组织内部共享。

在阅读本章之前,建议您首先阅读学习基础知识并完成教程

插件简介

插件可以来自 Gradle 或 Gradle 社区。但当用户想要组织他们的构建逻辑或需要现有插件未提供的特定构建功能时,他们可以开发自己的插件。

因此,我们区分三种不同类型的插件:

  1. 核心插件 - 来自 Gradle 的插件。

  2. 社区插件 - 来自 Gradle 插件门户或公共仓库的插件。

  3. 本地或自定义插件 - 您自己开发的插件。

核心插件

核心插件一词指的是 Gradle 发行版的一部分的插件,例如Java 库插件。它们始终可用。

社区插件

社区插件一词指的是发布到 Gradle 插件门户(或其他公共仓库)的插件,例如Spotless 插件

本地或自定义插件

本地或自定义插件一词指的是您为自己的构建编写的插件。

自定义插件

自定义插件有三种类型:

# 类型 位置 最可能 优点

1

脚本插件

一个 `.gradle(.kts)` 脚本文件

一个本地插件

插件会自动编译并包含在构建脚本的类路径中。

2

预编译脚本插件

`buildSrc` 文件夹或复合构建

一个约定插件

插件会自动编译、测试,并在构建脚本的类路径中可用。该插件对构建使用的每个构建脚本都可见。

3

二进制插件

独立项目

一个共享插件

生成并发布插件 JAR。该插件可用于多个构建并与他人共享。

脚本插件

脚本插件通常是小型本地插件,用脚本文件编写,用于单个构建或项目特定的任务。它们不需要在多个项目之间重用。脚本插件不推荐使用,但许多其他形式的插件都是从脚本插件演变而来的。

要创建插件,您需要编写一个实现 Plugin 接口的类。

以下示例创建了一个 `GreetingPlugin`,它在应用时向项目添加一个 `hello` 任务:

build.gradle.kts
class GreetingPlugin : Plugin<Project> {
    override fun apply(project: Project) {
        project.task("hello") {
            doLast {
                println("Hello from the GreetingPlugin")
            }
        }
    }
}

// Apply the plugin
apply<GreetingPlugin>()
build.gradle
class GreetingPlugin implements Plugin<Project> {
    void apply(Project project) {
        project.task('hello') {
            doLast {
                println 'Hello from the GreetingPlugin'
            }
        }
    }
}

// Apply the plugin
apply plugin: GreetingPlugin
$ gradle -q hello
Hello from the GreetingPlugin

`Project` 对象作为参数传递给 `apply()` 方法,插件可以使用它来根据需要配置项目(例如添加任务、配置依赖项等)。在此示例中,插件直接写入构建文件,这不是推荐的做法

当插件写入单独的脚本文件时,可以使用 `apply(from = "file_name.gradle.kts")` 或 `apply from: 'file_name.gradle'` 应用。在下面的示例中,插件在 `other.gradle(.kts)` 脚本文件中编码。然后,`other.gradle(.kts)` 使用 `apply from` 应用到 `build.gradle(.kts)` 中。

other.gradle.kts
class GreetingScriptPlugin : Plugin<Project> {
    override fun apply(project: Project) {
        project.task("hi") {
            doLast {
                println("Hi from the GreetingScriptPlugin")
            }
        }
    }
}

// Apply the plugin
apply<GreetingScriptPlugin>()
other.gradle
class GreetingScriptPlugin implements Plugin<Project> {
    void apply(Project project) {
        project.task('hi') {
            doLast {
                println 'Hi from the GreetingScriptPlugin'
            }
        }
    }
}

// Apply the plugin
apply plugin: GreetingScriptPlugin
build.gradle.kts
apply(from = "other.gradle.kts")
build.gradle
apply from: 'other.gradle'
$ gradle -q hi
Hi from the GreetingScriptPlugin

应避免使用脚本插件。

预编译脚本插件

预编译脚本插件在执行之前会被编译成类文件并打包成 JAR。这些插件使用 Groovy DSL 或 Kotlin DSL,而不是纯 Java、Kotlin 或 Groovy。它们最适合用作在项目之间共享构建逻辑的约定插件,或作为整齐组织构建逻辑的一种方式。

要创建预编译脚本插件,您可以:

  1. 使用 Gradle 的 Kotlin DSL - 插件是 `.gradle.kts` 文件,并应用 `kotlin-dsl`。

  2. 使用 Gradle 的 Groovy DSL - 插件是 `.gradle` 文件,并应用 `id("groovy-gradle-plugin")`。

要应用预编译脚本插件,您需要知道它的 ID。ID 是从插件脚本的文件名及其(可选)包声明派生的。

例如,脚本 `src/main/*/some-java-library.gradle(.kts)` 的插件 ID 是 `some-java-library`(假设它没有包声明)。同样,`src/main/*/my/some-java-library.gradle(.kts)` 的插件 ID 是 `my.some-java-library`,只要它有一个 `my` 的包声明。

预编译脚本插件名称有两个重要的限制:

  • 它们不能以 `org.gradle` 开头。

  • 它们不能与核心插件同名。

当插件应用于项目时,Gradle 会创建插件类的一个实例并调用该实例的 Plugin.apply() 方法。

在每个应用该插件的项目中,都会创建一个新的 `Plugin` 实例。

让我们将 `GreetingPlugin` 脚本插件重写为预编译脚本插件。由于我们使用 Groovy 或 Kotlin DSL,因此该文件基本上成为了插件。原始脚本插件只是创建了一个打印问候语的 `hello` 任务,这就是我们将在预编译脚本插件中执行的操作:

buildSrc/src/main/kotlin/GreetingPlugin.gradle.kts
tasks.register("hello") {
    doLast {
        println("Hello from the convention GreetingPlugin")
    }
}
buildSrc/src/main/groovy/GreetingPlugin.gradle
tasks.register("hello") {
    doLast {
        println("Hello from the convention GreetingPlugin")
    }
}

现在可以通过其 ID 在其他子项目的构建中应用 `GreetingPlugin`:

app/build.gradle.kts
plugins {
    application
    id("GreetingPlugin")
}
app/build.gradle
plugins {
    id 'application'
    id('GreetingPlugin')
}
$ gradle -q hello
Hello from the convention GreetingPlugin

约定插件

约定插件通常是一个预编译脚本插件,它使用您自己的约定(即默认值)配置现有的核心插件和社区插件,例如通过 `java.toolchain.languageVersion = JavaLanguageVersion.of(17)` 设置 Java 版本。约定插件还用于强制项目标准并帮助简化构建过程。它们可以应用和配置插件、创建新任务和扩展、设置依赖项等等。

让我们举一个包含三个子项目的示例构建:一个用于 `data-model`,一个用于 `database-logic`,一个用于 `app` 代码。项目结构如下:

.
├── buildSrc
│   ├── src
│   │   └──...
│   └── build.gradle.kts
├── data-model
│   ├── src
│   │   └──...
│   └── build.gradle.kts
├── database-logic
│   ├── src
│   │   └──...
│   └── build.gradle.kts
├── app
│   ├── src
│   │   └──...
│   └── build.gradle.kts
└── settings.gradle.kts

`database-logic` 子项目的构建文件如下:

database-logic/build.gradle.kts
plugins {
    id("java-library")
    id("org.jetbrains.kotlin.jvm") version "2.2.0"
}

repositories {
    mavenCentral()
}

java {
    toolchain.languageVersion.set(JavaLanguageVersion.of(11))
}

tasks.test {
    useJUnitPlatform()
}

kotlin {
    jvmToolchain(11)
}

// More build logic
database-logic/build.gradle
plugins {
    id 'java-library'
    id 'org.jetbrains.kotlin.jvm' version '2.2.0'
}

repositories {
    mavenCentral()
}

java {
    toolchain.languageVersion.set(JavaLanguageVersion.of(11))
}

tasks.test {
    useJUnitPlatform()
}

kotlin {
    jvmToolchain {
        languageVersion.set(JavaLanguageVersion.of(11))
    }
}

// More build logic

我们应用 `java-library` 插件并添加 `org.jetbrains.kotlin.jvm` 插件以支持 Kotlin。我们还配置 Kotlin、Java、测试等。

我们的构建文件开始变得越来越大……

我们应用的插件越多,配置的插件越多,构建文件就越大。在 `app` 和 `data-model` 子项目的构建文件中也存在重复,尤其是在配置诸如设置 Java 版本和 Kotlin 支持等常见扩展时。

为了解决这个问题,我们使用约定插件。这使我们能够避免在每个构建文件中重复配置,并使我们的构建脚本更简洁、更易于维护。在约定插件中,我们可以封装任意构建配置或自定义构建逻辑。

要开发约定插件,我们建议使用`buildSrc`——它代表一个完全独立的 Gradle 构建。`buildSrc` 有自己的设置文件来定义此构建的依赖项所在的位置。

我们在 `buildSrc/src/main/kotlin` 目录中添加一个名为 `my-java-library.gradle.kts` 的 Kotlin 脚本。或者,在 `buildSrc/src/main/groovy` 目录中添加一个名为 `my-java-library.gradle` 的 Groovy 脚本。我们将 `database-logic` 构建文件中的所有插件应用和配置放入其中。

buildSrc/src/main/kotlin/my-java-library.gradle.kts
plugins {
    id("java-library")
    id("org.jetbrains.kotlin.jvm")
}

repositories {
    mavenCentral()
}

java {
    toolchain.languageVersion.set(JavaLanguageVersion.of(11))
}

tasks.test {
    useJUnitPlatform()
}

kotlin {
    jvmToolchain(11)
}
buildSrc/src/main/groovy/my-java-library.gradle
plugins {
    id 'java-library'
    id 'org.jetbrains.kotlin.jvm'
}

repositories {
    mavenCentral()
}

java {
    toolchain.languageVersion.set(JavaLanguageVersion.of(11))
}

tasks.test {
    useJUnitPlatform()
}

kotlin {
    jvmToolchain {
        languageVersion.set(JavaLanguageVersion.of(11))
    }
}

文件名 `my-java-library` 是我们全新插件的 ID,我们现在可以在所有子项目中使用它。

为什么缺少 `id 'org.jetbrains.kotlin.jvm'` 的版本?请参阅将外部插件应用于预编译脚本插件

通过删除所有冗余构建逻辑并应用我们的约定 `my-java-library` 插件,`database-logic` 构建文件变得简单得多:

database-logic/build.gradle.kts
plugins {
    id("my-java-library")
}
database-logic/build.gradle
plugins {
    id('my-java-library')
}

这个约定插件使我们能够轻松地在所有构建文件中共享通用配置。任何修改都可以在一个地方进行,从而简化维护。

二进制插件

Gradle 中的二进制插件是构建为独立 JAR 文件并通过构建脚本中的 `plugins{}` 块应用于项目的插件。

让我们将 `GreetingPlugin` 移到一个独立的项目中,以便我们可以发布它并与他人共享。该插件基本上从 `buildSrc` 文件夹移到它自己的构建 `greeting-plugin` 中。

您可以从 `buildSrc` 发布插件,但这不推荐。准备发布的插件应该有自己的构建。

`greeting-plugin` 只是一个 Java 项目,它生成一个包含插件类的 JAR。

将插件打包并发布到仓库的最简单方法是使用Gradle 插件开发插件。此插件提供了必要的任务和配置(包括插件元数据),以将您的脚本编译成可在其他构建中应用的插件。

以下是使用 Gradle 插件开发插件的 `greeting-plugin` 项目的简单构建脚本:

build.gradle.kts
plugins {
    `java-gradle-plugin`
}

gradlePlugin {
    plugins {
        create("simplePlugin") {
            id = "org.example.greeting"
            implementationClass = "org.example.GreetingPlugin"
        }
    }
}
build.gradle
plugins {
    id 'java-gradle-plugin'
}

gradlePlugin {
    plugins {
        simplePlugin {
            id = 'org.example.greeting'
            implementationClass = 'org.example.GreetingPlugin'
        }
    }
}

有关发布插件的更多信息,请参阅发布插件

项目、设置与初始化插件的区别

在本节使用的示例中,插件接受 Project 类型作为类型参数。或者,插件可以接受类型为 Settings 的参数,以便在 settings 脚本中应用;或者接受类型为 Gradle 的参数,以便在初始化脚本中应用。

这些类型插件之间的区别在于它们的应用范围:

项目插件

项目插件是应用于构建中特定项目的插件。它可以自定义构建逻辑、添加任务和配置项目特定设置。

设置插件

设置插件是应用于 `settings.gradle` 或 `settings.gradle.kts` 文件的插件。它可以配置适用于整个构建的设置,例如定义构建中包含哪些项目、配置构建脚本仓库以及将通用配置应用于所有项目。

初始化插件

初始化插件是应用于 `init.gradle` 或 `init.gradle.kts` 文件的插件。它可以配置适用于机器上所有 Gradle 构建的全局设置,例如配置 Gradle 版本、设置默认仓库或将通用插件应用于所有构建。