Gradle 的许多功能都是通过插件提供的,包括随 Gradle 分发的核心插件、第三方插件以及在构建中定义的脚本插件。

插件引入了新任务(例如 JavaCompile)、领域对象(例如 SourceSet)、约定(例如在 src/main/java 中查找 Java 源),并扩展了核心或其他插件对象。

Gradle 中的插件对于自动化常见构建任务、与外部工具或服务集成以及根据特定项目需求定制构建过程至关重要。它们还充当组织构建逻辑的主要机制。

插件的优点

在构建脚本中编写许多任务和复制配置块可能会很混乱。与直接向构建脚本添加逻辑相比,插件提供了以下几个优点

  • 促进可重用性:减少了在项目中复制类似逻辑的需要。

  • 增强模块化:允许构建脚本更加模块化和有条理。

  • 封装逻辑:将命令式逻辑分开,启用更多声明式构建脚本。

插件分发

你可以利用 Gradle 和 Gradle 社区的插件,也可以创建自己的插件。

插件有三种方式可用

  1. 核心插件 - Gradle 开发并维护一组核心插件

  2. 社区插件 - 在远程仓库(如 Maven 或Gradle 插件门户)中共享的 Gradle 插件。

  3. 本地插件 - Gradle 允许用户使用API创建自定义插件。

插件类型

插件可以实现为二进制插件预编译脚本插件脚本插件

二进制插件

二进制插件是编译的插件,通常用 Java 或 Kotlin DSL 编写,并打包为 JAR 文件。它们使用 plugins {} 块应用于项目。与脚本插件或预编译脚本插件相比,它们提供了更好的性能和可维护性。

预编译脚本插件

预编译脚本插件是 Groovy DSL 或 Kotlin DSL 脚本,编译并分发为打包在库中的 Java 类文件。它们使用 plugins {} 块应用于项目。它们提供了一种在项目中重用复杂逻辑的方法,并允许更好地组织构建逻辑。

脚本插件

脚本插件是 Groovy DSL 或 Kotlin DSL 脚本,使用 apply from: 语法直接应用于 Gradle 构建脚本。它们在构建脚本中内联应用,以添加功能或自定义构建过程。它们易于使用。

插件通常从脚本插件开始(因为它们易于编写)。然后,随着代码变得更有价值,它会迁移到一个二进制插件,该插件可以在多个项目或组织之间轻松测试和共享。

使用插件

要使用插件中封装的构建逻辑,Gradle 需要执行两个步骤。首先,它需要解析插件,然后它需要将插件应用到目标,通常是Project

  1. 解析插件意味着找到包含给定插件的 JAR 的正确版本,并将其添加到脚本类路径。一旦解析了一个插件,就可以在构建脚本中使用其 API。脚本插件是自解析的,因为它们是从应用它们时提供的特定文件路径或 URL 解析的。作为 Gradle 分发的一部分提供的核心二进制插件会自动解析。

  2. 应用插件意味着对项目执行插件的 Plugin.apply(T)

推荐使用 插件 DSL 一步解决和应用插件。

解决插件

Gradle 提供 核心插件(例如 JavaPluginGroovyPluginMavenPublishPlugin 等)作为其分发的一部分,这意味着它们会自动解决。

在构建脚本中使用插件名称应用核心插件

plugins {
    id «plugin name»
}

例如

build.gradle
plugins {
    id("java")
}

非核心插件必须在应用之前解决。非核心插件在构建文件中通过唯一 ID 和版本标识

plugins {
    id «plugin id» version «plugin version»
}

并且插件的位置必须在设置文件中指定

settings.gradle
pluginManagement {
    repositories {
        gradlePluginPortal()
    }
    maven {
        url 'https://maven.example.com/plugins'
    }
}

解决和应用插件还有其他注意事项

# 使用 例如

1

核心社区本地插件应用到特定项目。

构建文件中的 plugins

plugins {
  id("org.barfuin.gradle.taskinfo") version "2.1.0"
}

2

将通用 核心社区本地插件应用到多个子项目。

buildSrc 目录中的构建脚本

plugins {
    id("org.barfuin.gradle.taskinfo") version "2.1.0"
}
repositories {
    jcenter()
}
dependencies {
    implementation(Libs.Kotlin.coroutines)
}

3

应用 构建脚本本身 所需的 核心社区本地插件。

构建文件中的 buildscript

buildscript {
  repositories {
    maven {
      url = uri("https://plugins.gradle.org.cn/m2/")
    }
  }
  dependencies {
    classpath("org.barfuin.gradle.taskinfo:gradle-taskinfo:2.1.0")
  }
}
plugins {
  id("org.barfuin.gradle.taskinfo") version "2.1.0"
}

4

应用 本地脚本插件。

构建文件中的旧版 apply() 方法

apply(plugin = "org.barfuin.gradle.taskinfo")
apply<MyPlugin>()

1. 使用 plugins{} 块应用插件

插件 DSL 提供了一种简洁方便的方式来声明插件依赖项。

插件块配置 PluginDependenciesSpec 的实例

plugins {
    application                                     // by name
    java                                            // by name
    id("java")                                      // by id - recommended
    id("org.jetbrains.kotlin.jvm") version "1.9.0"  // by id - recommended
}

核心 Gradle 插件的独特之处在于它们提供短名称,例如核心 JavaPluginjava

要应用核心插件,可以使用短 名称

build.gradle.kts
plugins {
    java
}
build.gradle
plugins {
    id 'java'
}

所有其他二进制插件都必须使用插件 ID 的完全限定形式(例如 com.github.foo.bar)。

要从 Gradle 插件门户 应用社区插件,必须使用完全限定的插件 ID,即全局唯一标识符

build.gradle.kts
plugins {
    id("com.jfrog.bintray") version "1.8.5"
}
build.gradle
plugins {
    id 'com.jfrog.bintray' version '1.8.5'
}

有关使用插件 DSL 的更多信息,请参见 PluginDependenciesSpec

插件 DSL 的限制

插件 DSL 为用户提供了便捷的语法,并且 Gradle 能够快速确定使用了哪些插件。这允许 Gradle

  • 优化插件类的加载和重用。

  • 向编辑器提供有关构建脚本中潜在属性和值的详细信息。

但是,DSL 要求插件被静态定义。

plugins {} 块机制和“传统”apply() 方法机制之间有一些关键差异。还有一些约束和可能的限制。

受限语法

plugins {} 块不支持任意代码。

它受限于幂等(每次产生相同的结果)且无副作用(Gradle 可随时安全执行)。

格式为

build.gradle.kts
plugins {
    id(«plugin id»)                             (1)
    id(«plugin id») version «plugin version»    (2)
}
1 对于核心 Gradle 插件或构建脚本中已有的插件
2 对于需要解析的二进制 Gradle 插件
build.gradle
plugins {
    id «plugin id»                            (1)
    id «plugin id» version «plugin version»   (2)
}
1 对于核心 Gradle 插件或构建脚本中已有的插件
2 对于需要解析的二进制 Gradle 插件

其中 «plugin id»«plugin version» 是字符串。

其中 «plugin id»«plugin version» 必须是常量、文字字符串。

plugins{} 块还必须是构建脚本中的顶级语句。它不能嵌套在另一个构造中(例如,if 语句或 for 循环)。

仅在构建脚本和设置文件中

plugins{} 块只能在项目的构建脚本 build.gradle(.kts)settings.gradle(.kts) 文件中使用。它必须出现在任何其他块之前。它不能在脚本插件或初始化脚本中使用。

将插件应用于所有子项目

假设您有一个多项目构建,您可能希望将插件应用到构建中的一些或所有子项目,但不应用到root项目。

虽然plugins{}块的默认行为是立即resolveapply插件,但您可以使用apply false语法告诉 Gradle 不要将插件应用到当前项目。然后,在子项目的构建脚本中使用不带版本的plugins{}

settings.gradle.kts
include("hello-a")
include("hello-b")
include("goodbye-c")
build.gradle.kts
plugins {
    id("com.example.hello") version "1.0.0" apply false
    id("com.example.goodbye") version "1.0.0" apply false
}
hello-a/build.gradle.kts
plugins {
    id("com.example.hello")
}
hello-b/build.gradle.kts
plugins {
    id("com.example.hello")
}
goodbye-c/build.gradle.kts
plugins {
    id("com.example.goodbye")
}
settings.gradle
include 'hello-a'
include 'hello-b'
include 'goodbye-c'
build.gradle
plugins {
    id 'com.example.hello' version '1.0.0' apply false
    id 'com.example.goodbye' version '1.0.0' apply false
}
hello-a/build.gradle
plugins {
    id 'com.example.hello'
}
hello-b/build.gradle
plugins {
    id 'com.example.hello'
}
goodbye-c/build.gradle
plugins {
    id 'com.example.goodbye'
}

您还可以通过使用自己的约定插件编写构建逻辑来封装外部插件的版本。

2. 从buildSrc目录应用插件

buildSrc是 Gradle 项目根目录中的一个可选目录,其中包含用于构建主项目的构建逻辑(即插件)。只要项目buildSrc目录中的插件具有已定义的 ID,您就可以应用它们。

以下示例展示了如何将插件实现类my.MyPlugin(在buildSrc中定义)绑定到 ID“my-plugin”

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

gradlePlugin {
    plugins {
        create("myPlugins") {
            id = "my-plugin"
            implementationClass = "my.MyPlugin"
        }
    }
}
buildSrc/build.gradle
plugins {
    id 'java-gradle-plugin'
}

gradlePlugin {
    plugins {
        myPlugins {
            id = 'my-plugin'
            implementationClass = 'my.MyPlugin'
        }
    }
}

然后可以通过 ID 应用插件

build.gradle.kts
plugins {
    id("my-plugin")
}
build.gradle
plugins {
    id 'my-plugin'
}

3. 使用buildscript{}块应用插件

buildscript块用于

  1. 构建项目所需的全局dependenciesrepositories(应用于子项目中)。

  2. 声明哪些插件可用于在构建脚本中(在build.gradle(.kts)文件中本身)。

因此,当您想在构建脚本本身中使用库时,您必须使用buildScript在脚本类路径上添加此库

import org.apache.commons.codec.binary.Base64

buildscript {
    repositories {  // this is where the plugins are located
        mavenCentral()
        google()
    }
    dependencies { // these are the plugins that can be used in subprojects or in the build file itself
        classpath group: 'commons-codec', name: 'commons-codec', version: '1.2' // used in the task below
        classpath 'com.android.tools.build:gradle:4.1.0' // used in subproject
    }
}

tasks.register('encode') {
    doLast {
        def byte[] encodedString = new Base64().encode('hello world\n'.getBytes())
        println new String(encodedString)
    }
}

您可以在需要它的子项目中应用全局声明的依赖项

plugins {
    id 'com.android.application'
}

通过将插件添加到构建脚本类路径,然后应用插件,可以将作为外部 jar 文件发布的二进制插件添加到项目中。

可以使用buildscript{}块将外部 jar 添加到构建脚本类路径,如构建脚本的外部依赖项中所述

build.gradle.kts
buildscript {
    repositories {
        gradlePluginPortal()
    }
    dependencies {
        classpath("com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.5")
    }
}

apply(plugin = "com.jfrog.bintray")
build.gradle
buildscript {
    repositories {
        gradlePluginPortal()
    }
    dependencies {
        classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.5'
    }
}

apply plugin: 'com.jfrog.bintray'

4. 使用旧版 apply() 方法应用脚本插件

脚本插件是一种临时插件,通常在同一构建脚本中编写和应用。它使用 旧版应用方法 应用

class MyPlugin : Plugin<Project> {
    override fun apply(project: Project) {
        println("Plugin ${this.javaClass.simpleName} applied on ${project.name}")
    }
}

apply<MyPlugin>()

我们来看一个基本示例,该插件编写在名为 other.gradle 的文件中,该文件位于与 build.gradle 文件相同的目录中

public class Other implements Plugin<Project> {
    @Override
    void apply(Project project) {
        // Does something
    }
}

首先,使用以下方法导入外部文件

apply from: 'other.gradle'

然后,您可以应用它

apply plugin: Other

脚本插件会自动解析,可以从本地文件系统或远程脚本应用

build.gradle.kts
apply(from = "other.gradle.kts")
build.gradle
apply from: 'other.gradle'

文件系统位置相对于项目目录,而远程脚本位置使用 HTTP URL 指定。可以将多个脚本插件(任何形式)应用到给定的目标。

插件管理

pluginManagement{} 块用于配置插件解析存储库,并为构建脚本中应用的插件定义版本约束。

pluginManagement{} 块可以在 settings.gradle(.kts) 文件中使用,其中它必须是文件中的第一个块

settings.gradle.kts
pluginManagement {
    plugins {
    }
    resolutionStrategy {
    }
    repositories {
    }
}
rootProject.name = "plugin-management"
settings.gradle
pluginManagement {
    plugins {
    }
    resolutionStrategy {
    }
    repositories {
    }
}
rootProject.name = 'plugin-management'

该块还可以在 初始化脚本 中使用

init.gradle.kts
settingsEvaluated {
    pluginManagement {
        plugins {
        }
        resolutionStrategy {
        }
        repositories {
        }
    }
}
init.gradle
settingsEvaluated { settings ->
    settings.pluginManagement {
        plugins {
        }
        resolutionStrategy {
        }
        repositories {
        }
    }
}

自定义插件存储库

默认情况下,plugins{} DSL 从公共 Gradle 插件门户 解析插件。

许多构建作者还希望从私有 Maven 或 Ivy 存储库解析插件,因为它们包含专有实现详细信息,或希望更好地控制构建中可用的插件。

要指定自定义插件存储库,请在 pluginManagement{} 内使用 repositories{}

settings.gradle.kts
pluginManagement {
    repositories {
        maven(url = "./maven-repo")
        gradlePluginPortal()
        ivy(url = "./ivy-repo")
    }
}
settings.gradle
pluginManagement {
    repositories {
        maven {
            url './maven-repo'
        }
        gradlePluginPortal()
        ivy {
            url './ivy-repo'
        }
    }
}

这告诉 Gradle 在解析插件时首先查看 ../maven-repo 中的 Maven 存储库,然后在 Maven 存储库中找不到插件时检查 Gradle 插件门户。如果您不希望搜索 Gradle 插件门户,请省略 gradlePluginPortal() 行。最后,将检查 ../ivy-repo 中的 Ivy 存储库。

插件版本管理

pluginManagement{} 内的 plugins{} 块允许在单个位置定义构建的所有插件版本。然后,可以通过 plugins{} 块按 ID 将插件应用到任何构建脚本。

设置插件版本的方式之一的好处是,pluginManagement.plugins{} 不具有与构建脚本 plugins{} 块相同的 受限语法。这允许从 gradle.properties 中获取插件版本,或通过其他机制加载插件版本。

通过 pluginManagement 管理插件版本

settings.gradle.kts
pluginManagement {
  val helloPluginVersion: String by settings
  plugins {
    id("com.example.hello") version "${helloPluginVersion}"
  }
}
build.gradle.kts
plugins {
    id("com.example.hello")
}
gradle.properties
helloPluginVersion=1.0.0
settings.gradle
pluginManagement {
  plugins {
        id 'com.example.hello' version "${helloPluginVersion}"
    }
}
build.gradle
plugins {
    id 'com.example.hello'
}
gradle.properties
helloPluginVersion=1.0.0

插件版本从 gradle.properties 中加载,并在设置脚本中配置,允许将插件添加到任何项目中,而无需指定版本。

插件解析规则

插件解析规则允许您修改在 plugins{} 块中发出的插件请求,例如,更改请求的版本或明确指定实现工件坐标。

要添加解析规则,请在 pluginManagement{} 块内使用 resolutionStrategy{}

settings.gradle.kts
pluginManagement {
    resolutionStrategy {
        eachPlugin {
            if (requested.id.namespace == "com.example") {
                useModule("com.example:sample-plugins:1.0.0")
            }
        }
    }
    repositories {
        maven {
            url = uri("./maven-repo")
        }
        gradlePluginPortal()
        ivy {
            url = uri("./ivy-repo")
        }
    }
}
settings.gradle
pluginManagement {
    resolutionStrategy {
        eachPlugin {
            if (requested.id.namespace == 'com.example') {
                useModule('com.example:sample-plugins:1.0.0')
            }
        }
    }
    repositories {
        maven {
            url './maven-repo'
        }
        gradlePluginPortal()
        ivy {
            url './ivy-repo'
        }
    }
}

这会告知 Gradle 使用指定的插件实现工件,而不是其从插件 ID 到 Maven/Ivy 坐标的内置默认映射。

自定义 Maven 和 Ivy 插件存储库必须包含 插件标记工件 和实现插件的工件。阅读 Gradle 插件开发插件,以获取有关将插件发布到自定义存储库的更多信息。

请参阅 PluginManagementSpec,以获取有关使用 pluginManagement{} 块的完整文档。

插件标记工件

由于 plugins{} DSL 块仅允许通过其全局唯一插件 idversion 属性来声明插件,因此 Gradle 需要一种方法来查找插件实现工件的坐标。

为此,Gradle 将查找具有坐标 plugin.id:plugin.id.gradle.plugin:plugin.version 的插件标记工件。此标记需要依赖于实际插件实现。发布这些标记由 java-gradle-plugin 自动完成。

例如,来自 sample-plugins 项目的以下完整示例展示了如何使用 java-gradle-pluginmaven-publish 插件和 ivy-publish 插件的组合,将 com.example.hello 插件和 com.example.goodbye 插件发布到 Ivy 和 Maven 存储库。

build.gradle.kts
plugins {
    `java-gradle-plugin`
    `maven-publish`
    `ivy-publish`
}

group = "com.example"
version = "1.0.0"

gradlePlugin {
    plugins {
        create("hello") {
            id = "com.example.hello"
            implementationClass = "com.example.hello.HelloPlugin"
        }
        create("goodbye") {
            id = "com.example.goodbye"
            implementationClass = "com.example.goodbye.GoodbyePlugin"
        }
    }
}

publishing {
    repositories {
        maven {
            url = uri(layout.buildDirectory.dir("maven-repo"))
        }
        ivy {
            url = uri(layout.buildDirectory.dir("ivy-repo"))
        }
    }
}
build.gradle
plugins {
    id 'java-gradle-plugin'
    id 'maven-publish'
    id 'ivy-publish'
}

group 'com.example'
version '1.0.0'

gradlePlugin {
    plugins {
        hello {
            id = 'com.example.hello'
            implementationClass = 'com.example.hello.HelloPlugin'
        }
        goodbye {
            id = 'com.example.goodbye'
            implementationClass = 'com.example.goodbye.GoodbyePlugin'
        }
    }
}

publishing {
    repositories {
        maven {
            url layout.buildDirectory.dir("maven-repo")
        }
        ivy {
            url layout.buildDirectory.dir("ivy-repo")
        }
    }
}

在示例目录中运行 gradle publish 会创建以下 Maven 存储库布局(Ivy 布局类似)

plugin markers

旧版插件应用程序

随着 插件 DSL 的引入,用户几乎没有理由使用旧版插件应用方法。如果构建作者由于当前工作方式的限制而无法使用插件 DSL,则此处记录了该方法。

build.gradle.kts
apply(plugin = "java")
build.gradle
apply plugin: 'java'

可以使用插件 ID应用插件。在上述情况下,我们使用简称“java”来应用 JavaPlugin

除了使用插件 ID,还可以通过简单地指定插件的类来应用插件

build.gradle.kts
apply<JavaPlugin>()
build.gradle
apply plugin: JavaPlugin

上述示例中的 JavaPlugin 符号指的是 JavaPlugin。此类不需要严格导入,因为 org.gradle.api.plugins 包在所有构建脚本中自动导入(请参阅 默认导入)。

此外,需要附加 ::class 后缀来标识 Kotlin 中的类文字,而不是 Java 中的 .class

此外,不需要附加 .class 来标识 Groovy 中的类文字,就像在 Java 中一样。

使用版本目录

当项目使用版本目录时,可以在应用时通过别名引用插件。

我们来看一个简单的版本目录

gradle/libs.versions.toml
[versions]
intellij-plugin = "1.6"

[plugins]
jetbrains-intellij = { id = "org.jetbrains.intellij", version.ref = "intellij-plugin" }

然后可以使用 alias 方法将插件应用到任何构建脚本

build.gradle.kts
plugins {
    alias(libs.plugins.jetbrains.intellij)
}
jetbrains-intellij 可用作 Gradle 生成的安全访问器:jetbrains.intellij

下一步: 了解如何编写插件 >>