Gradle 的许多功能通过插件实现,包括与 Gradle 一起分发的核心插件、第三方插件以及在构建中定义的脚本插件。
插件引入了新的 Task(例如,JavaCompile
)、领域对象(例如,SourceSet
)、约定(例如,在 src/main/java
中定位 Java 源码),并扩展了核心或其他插件对象。
Gradle 中的插件对于自动化常见的构建 Task、与外部工具或服务集成以及根据特定项目需求定制构建过程至关重要。它们也是组织构建逻辑的主要机制。
插件的好处
在构建脚本中编写大量 Task 并复制配置块可能会变得混乱。与直接将逻辑添加到构建脚本相比,插件提供了几个优势
-
促进重用性:减少了在不同项目之间重复类似逻辑的需求。
-
增强模块化:使构建脚本更具模块化和组织性。
-
封装逻辑:将命令式逻辑分开,从而实现更具声明性的构建脚本。
插件分发
您可以使用来自 Gradle 和 Gradle 社区的插件,也可以创建自己的插件。
插件可以通过三种方式获取
-
核心插件 - Gradle 开发和维护了一系列核心插件。
-
社区插件 - 在远程仓库(如 Maven 或Gradle 插件门户)中共享的 Gradle 插件。
-
自定义插件 - Gradle 允许用户使用API创建插件。
插件类型
插件可以实现为二进制插件、预编译脚本插件或脚本插件
1. 脚本插件
脚本插件是 Groovy DSL 或 Kotlin DSL 脚本,使用 apply from:
语法直接应用到 Gradle 构建脚本中。它们在构建脚本内联应用,用于添加功能或定制构建过程。不推荐使用脚本插件,但了解它们的工作方式很重要
// Define a plugin
class HelloWorldPlugin : Plugin<Project> {
override fun apply(project: Project) {
project.tasks.register("helloWorld") {
group = "Example"
description = "Prints 'Hello, World!' to the console"
doLast {
println("Hello, World!")
}
}
}
}
// Apply the plugin
apply<HelloWorldPlugin>()
2. 预编译脚本插件
预编译脚本插件是 Groovy DSL 或 Kotlin DSL 脚本,被编译并作为 Java 类文件打包在库中进行分发。它们旨在作为二进制 Gradle 插件使用,因此使用 plugins {}
块应用到项目中。预编译脚本可引用的插件 ID 源自其名称和可选的包声明。
// This script is automatically exposed to downstream consumers as the `my-plugin` plugin
tasks {
register("myCopyTask", Copy::class) {
group = "sample"
from("build.gradle.kts")
into("build/copy")
}
}
plugins {
id("my-plugin") version "1.0"
}
3. BuildSrc
和约定插件
它们是预编译插件和二进制插件的混合体,提供了一种在项目之间重用复杂逻辑的方法,并能更好地组织构建逻辑。
plugins {
java
}
repositories {
mavenCentral()
}
dependencies {
testImplementation("org.junit.jupiter:junit-jupiter:5.8.1")
implementation("com.google.guava:guava:30.1.1-jre")
}
tasks.named<Test>("test") {
useJUnitPlatform()
}
tasks.register<Copy>("backupTestXml") {
from("build/test-results/test")
into("/tmp/results/")
exclude("binary/**")
}
plugins {
application
id("shared-build-conventions")
}
4. 二进制插件
二进制插件是编译后的插件,通常使用 Java 或 Kotlin DSL 编写,并打包成 JAR 文件。它们使用 plugins {}
块应用到项目中。与脚本插件或预编译脚本插件相比,它们提供了更好的性能和可维护性。
class MyPlugin : Plugin<Project> {
override fun apply(project: Project) {
project.run {
tasks {
register("myCopyTask", Copy::class) {
group = "sample"
from("build.gradle.kts")
into("build/copy")
}
}
}
}
}
plugins {
id("my-plugin") version "1.0"
}
二进制插件和脚本插件的区别在于它们的共享和执行方式
-
二进制插件被编译成字节码,并共享字节码。
-
脚本插件以源代码形式共享,并在使用时编译。
二进制插件可以用任何生成 JVM 字节码的语言编写,例如 Java、Kotlin 或 Groovy。相比之下,脚本插件只能使用 Kotlin DSL 或 Groovy DSL 编写。
然而,也存在一种中间方案:预编译脚本插件。它们像脚本插件一样用 Kotlin DSL 或 Groovy DSL 编写,但像二进制插件一样编译成字节码并共享。
插件通常从脚本插件开始(因为它们易于编写)。然后,随着代码变得更有价值,会迁移到二进制插件,后者可以在多个项目或组织之间轻松测试和共享。
使用插件
要使用插件中封装的构建逻辑,Gradle 需要执行两个步骤。首先,它需要解析插件,然后需要将插件应用到目标(通常是Project
)。
-
解析插件意味着找到包含给定插件的 JAR 的正确版本,并将其添加到脚本类路径中。一旦插件被解析,就可以在构建脚本中使用其 API。脚本插件是自解析的,因为它们在应用时会从提供的特定文件路径或 URL 进行解析。作为 Gradle 分发一部分提供的核心二进制插件会自动解析。
-
应用插件意味着在项目上执行插件的 Plugin.apply(T) 方法。
推荐使用plugins DSL一步完成插件的解析和应用。
应用插件
Gradle 将核心插件(例如,JavaPlugin
、GroovyPlugin
、MavenPublishPlugin
等)作为其分发的一部分提供,这意味着它们会自动解析。
核心插件在构建脚本中使用插件名称应用
plugins {
id «plugin name»
}
例如
plugins {
id("java")
}
plugins {
id 'java'
}
非核心插件必须在应用之前解析。非核心插件在构建文件中通过唯一的 ID 和版本标识
plugins {
id «plugin id» version «plugin version»
}
例如
plugins {
id("com.gradleup.shadow") version "8.3.4"
}
plugins {
id 'com.gradleup.shadow' version '8.3.4'
}
并且插件的位置必须在 settings 文件中指定
pluginManagement { (1)
repositories {
gradlePluginPortal()
}
}
pluginManagement { (1)
repositories {
gradlePluginPortal()
}
}
解析和应用插件还有其他注意事项
# | 目标 | 使用 | 例如 |
---|---|---|---|
将插件应用到项目。 |
plugins { id("org.barfuin.gradle.taskinfo") version "2.1.0" } |
||
将插件应用到多个项目。 |
plugins { id("org.barfuin.gradle.taskinfo") version "2.1.0" } allprojects { apply(plugin = "org.barfuin.gradle.taskinfo") repositories { mavenCentral() } } |
||
将插件应用到多个项目。 |
plugins { id("com.gradleup.shadow") version "8.3.4" apply false id("io.ratpack.ratpack-java") version "1.8.2" apply false } |
||
将插件应用到多个项目。 |
plugins { id("my-convention.gradle.taskinfo") } |
||
应用构建脚本自身所需的插件。 |
buildscript { repositories { mavenCentral() } dependencies { classpath("org.barfuin.gradle.taskinfo:gradle-taskinfo:2.1.0") } } apply(plugin = "org.barfuin.gradle.taskinfo") |
||
应用脚本插件。 在类型安全访问器不可用时应用插件。 |
构建文件中的传统 |
apply<MyCustomBarfuinTaskInfoPlugin>() |
1. 使用 plugins{}
块应用插件
插件 DSL 提供了一种简洁方便的方式来声明插件依赖。
plugins 块配置了 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 插件的独特之处在于它们提供了短名称,例如核心 JavaPlugin 使用 java
。
要应用核心插件,可以使用短名称
plugins {
java
}
plugins {
id 'java'
}
所有其他二进制插件必须使用插件 ID 的完全限定形式(例如,com.github.foo.bar
)。
要从Gradle 插件门户应用社区插件,必须使用完全限定的插件 ID,这是一个全局唯一标识符
plugins {
id("org.springframework.boot") version "3.3.1"
}
plugins {
id 'org.springframework.boot' version '3.3.1'
}
有关使用插件 DSL 的更多信息,请参阅 PluginDependenciesSpec
。
plugins DSL 的局限性
plugins DSL 为用户提供了方便的语法,并使 Gradle 能够快速确定使用了哪些插件。这使得 Gradle 可以
-
优化插件类的加载和重用。
-
为编辑器提供关于构建脚本中潜在属性和值的详细信息。
然而,该 DSL 要求插件是静态定义的。
plugins {}
块机制与“传统”的 apply()
方法机制之间存在一些关键区别。还存在一些约束和可能的局限性。
plugins{}
块只能用在项目的构建脚本 build.gradle(.kts)
和 settings.gradle(.kts)
文件中。它必须出现在任何其他块之前。它不能用于脚本插件或 init 脚本。
受限语法
plugins {}
块不支持任意代码。
它被限制为幂等的(每次产生相同的结果)和无副作用的(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 循环)。
2. 将插件应用到所有 subprojects{} 或 allprojects{}
假设您有一个多项目构建,您可能希望将插件应用到构建中的部分或全部子项目,而不是应用到 root
项目。
虽然 plugins{}
块的默认行为是立即解析并应用插件,但您可以使用 apply false
语法告知 Gradle 不要将插件应用到当前项目。然后,在子项目的构建脚本中使用不带版本的 plugins{}
块
include("hello-a")
include("hello-b")
include("goodbye-c")
plugins {
// These plugins are not automatically applied.
// They can be applied in subprojects as needed (in their respective build files).
id("com.example.hello") version "1.0.0" apply false
id("com.example.goodbye") version "1.0.0" apply false
}
allprojects {
// Apply the common 'java' plugin to all projects (including the root)
plugins.apply("java")
}
subprojects {
// Apply the 'java-library' plugin to all subprojects (excluding the root)
plugins.apply("java-library")
}
plugins {
id("com.example.hello")
}
plugins {
id("com.example.hello")
}
plugins {
id("com.example.goodbye")
}
include 'hello-a'
include 'hello-b'
include 'goodbye-c'
plugins {
// These plugins are not automatically applied.
// They can be applied in subprojects as needed (in their respective build files).
id 'com.example.hello' version '1.0.0' apply false
id 'com.example.goodbye' version '1.0.0' apply false
}
allprojects {
// Apply the common 'java' plugin to all projects (including the root)
apply(plugin: 'java')
}
subprojects {
// Apply the 'java-library' plugin to all subprojects (excluding the root)
apply(plugin: 'java-library')
}
plugins {
id 'com.example.hello'
}
plugins {
id 'com.example.hello'
}
plugins {
id 'com.example.goodbye'
}
您还可以通过使用自己的约定插件组合构建逻辑来封装外部插件的版本。
3. 应用在根项目中声明的插件
在多项目构建中,您可以从根项目或父项目应用插件,以便与其他项目共享通用逻辑和行为。根项目/父项目是目录层次结构顶部的项目。
您应该使用 plugins {}
块,因为它确保插件在项目评估阶段之前应用和配置。这样,您可以安全地使用插件引入的任何模型元素的类型安全访问器
rootProject.name = "multi-project-build"
include("domain", "infra", "http")
plugins {
id("com.gradleup.shadow") version "8.3.4" apply false
id("io.ratpack.ratpack-java") version "1.8.2" apply false
}
plugins {
`java-library`
}
dependencies {
api("javax.measure:unit-api:1.0")
implementation("tec.units:unit-ri:1.0.3")
}
plugins {
`java-library`
id("com.gradleup.shadow")
}
plugins {
java
id("io.ratpack.ratpack-java")
}
dependencies {
implementation(project(":domain"))
implementation(project(":infra"))
implementation(ratpack.dependency("dropwizard-metrics"))
}
在根项目/父项目的 build.gradle(.kts)
中,插件被声明但未应用(通过 apply false
)。这种方法虽然是可选的,但使得插件可以在特定的子项目中被显式应用。如果没有 apply false
,在根项目中声明的插件将无法仅显式地应用到某些子项目。
apply false 是可选的。如果在根构建文件中声明插件时没有使用 apply false ,这些插件将自动应用到根项目。 |
在 infra
子项目中,显式应用了在根项目中可用的 com.gradleup.shadow
插件。http
子项目应用 io.ratpack.ratpack-java
。domain
子项目没有应用根项目中的插件。
4. 应用来自 buildSrc
目录的约定插件
buildSrc
是 Gradle 项目根目录下的一个可选目录,包含用于构建主项目的构建逻辑(即插件)。您可以应用位于项目 buildSrc
目录中的插件,只要它们具有定义的 ID。
以下示例展示了如何将定义在 buildSrc
中的插件实现类 my.MyPlugin
与 ID "my-plugin" 关联起来
plugins {
`java-gradle-plugin`
}
gradlePlugin {
plugins {
create("myPlugins") {
id = "my-plugin"
implementationClass = "my.MyPlugin"
}
}
}
plugins {
id 'java-gradle-plugin'
}
gradlePlugin {
plugins {
myPlugins {
id = 'my-plugin'
implementationClass = 'my.MyPlugin'
}
}
}
然后可以通过 ID 应用插件
plugins {
id("my-plugin")
}
plugins {
id 'my-plugin'
}
5. 使用 buildscript{}
块应用插件
要定义构建脚本自身使用的库或插件,可以使用 buildscript
块。buildscript
块也用于指定在哪里查找这些依赖。
随着 Gradle 新版本的发布,这种方法已不太常用,因为 plugins {}
块简化了插件的使用。然而,在处理自定义或非标准插件仓库以及库依赖时,可能仍然需要使用 buildscript {}
。
import org.yaml.snakeyaml.Yaml
import java.io.File
buildscript {
repositories {
maven {
url = uri("https://plugins.gradle.org/m2/")
}
mavenCentral() // Where to find the plugin
}
dependencies {
classpath("org.yaml:snakeyaml:1.19") // The library's classpath dependency
classpath("com.gradleup.shadow:shadow-gradle-plugin:8.3.4") // Plugin dependency for legacy plugin application
}
}
// Applies legacy Shadow plugin
apply(plugin = "com.gradleup.shadow")
// Uses the library in the build script
val yamlContent = """
name: Project
""".trimIndent()
val yaml = Yaml()
val data: Map<String, Any> = yaml.load(yamlContent)
import org.yaml.snakeyaml.Yaml
buildscript {
repositories { // Where to find the plugin or library
maven {
url = uri("https://plugins.gradle.org/m2/")
}
mavenCentral()
}
dependencies {
classpath 'org.yaml:snakeyaml:1.19' // The library's classpath dependency
classpath 'com.gradleup.shadow:shadow-gradle-plugin:8.3.4' // Plugin dependency for legacy plugin application
}
}
// Applies legacy Shadow plugin
apply plugin: 'com.gradleup.shadow'
// Uses the library in the build script
def yamlContent = """
name: Project Name
"""
def yaml = new Yaml()
def data = yaml.load(yamlContent)
6. 使用传统 apply()
方法应用脚本插件
脚本插件是一种临时插件,通常在同一个构建脚本中编写和应用。它使用传统应用方法应用
class MyPlugin : Plugin<Project> {
override fun apply(project: Project) {
println("Plugin ${this.javaClass.simpleName} applied on ${project.name}")
}
}
apply<MyPlugin>()
class MyPlugin implements Plugin<Project> {
@Override
void apply(Project project) {
println("Plugin ${this.getClass().getSimpleName()} applied on ${project.name}")
}
}
apply plugin: MyPlugin
插件管理
pluginManagement{}
块用于配置插件解析的仓库,以及为在构建脚本中应用的插件定义版本约束。
pluginManagement{}
块可以用在 settings.gradle(.kts)
文件中,它必须是文件中的第一个块
pluginManagement {
plugins {
}
resolutionStrategy {
}
repositories {
}
}
rootProject.name = "plugin-management"
pluginManagement {
plugins {
}
resolutionStrategy {
}
repositories {
}
}
rootProject.name = 'plugin-management'
该块也可以用于初始化脚本
settingsEvaluated {
pluginManagement {
plugins {
}
resolutionStrategy {
}
repositories {
}
}
}
settingsEvaluated { settings ->
settings.pluginManagement {
plugins {
}
resolutionStrategy {
}
repositories {
}
}
}
自定义插件仓库
默认情况下,plugins{}
DSL 从公共的Gradle 插件门户解析插件。
许多构建作者还希望从私有 Maven 或 Ivy 仓库解析插件,因为它们包含专有的实现细节,或者希望对构建可用的插件拥有更多控制权。
要指定自定义插件仓库,请在 pluginManagement{}
内使用 repositories{}
块
pluginManagement {
repositories {
maven(url = file("./maven-repo"))
gradlePluginPortal()
ivy(url = file("./ivy-repo"))
}
}
pluginManagement {
repositories {
maven {
url = file('./maven-repo')
}
gradlePluginPortal()
ivy {
url = file('./ivy-repo')
}
}
}
这告诉 Gradle 在解析插件时首先查看位于 ../maven-repo
的 Maven 仓库,如果在 Maven 仓库中找不到插件,则检查 Gradle 插件门户。如果您不想搜索 Gradle 插件门户,请省略 gradlePluginPortal()
行。最后,将检查位于 ../ivy-repo
的 Ivy 仓库。
插件版本管理
pluginManagement{}
内的 plugins{}
块允许在单个位置定义构建的所有插件版本。然后可以通过 ID 使用 plugins{}
块将插件应用到任何构建脚本。
以这种方式设置插件版本的一个好处是 pluginManagement.plugins{}
不像构建脚本 plugins{}
块那样具有相同的受限语法。这使得可以从 gradle.properties
获取插件版本,或通过其他机制加载。
通过 pluginManagement
管理插件版本
pluginManagement {
val helloPluginVersion: String by settings
plugins {
id("com.example.hello") version "${helloPluginVersion}"
}
}
plugins {
id("com.example.hello")
}
helloPluginVersion=1.0.0
pluginManagement {
plugins {
id 'com.example.hello' version "${helloPluginVersion}"
}
}
plugins {
id 'com.example.hello'
}
helloPluginVersion=1.0.0
插件版本从 gradle.properties
加载并在 settings 脚本中配置,从而允许将插件添加到任何项目而无需指定版本。
插件解析规则
插件解析规则允许您修改在 plugins{}
块中发出的插件请求,例如,更改请求的版本或显式指定实现 artifact 的坐标。
要添加解析规则,请在 pluginManagement{}
块内使用 resolutionStrategy{}
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")
}
}
}
pluginManagement {
resolutionStrategy {
eachPlugin {
if (requested.id.namespace == 'com.example') {
useModule('com.example:sample-plugins:1.0.0')
}
}
}
repositories {
maven {
url = file('./maven-repo')
}
gradlePluginPortal()
ivy {
url = file('./ivy-repo')
}
}
}
这告诉 Gradle 使用指定的插件实现 artifact,而不是使用其内置的从插件 ID 到 Maven/Ivy 坐标的默认映射。
自定义 Maven 和 Ivy 插件仓库必须包含插件标记 artifact 以及实现插件的 artifact。有关将插件发布到自定义仓库的更多信息,请阅读Gradle 插件开发插件。
有关使用 pluginManagement{}
块的完整文档,请参阅 PluginManagementSpec。
插件标记 Artifact
由于 plugins{}
DSL 块只允许通过其全局唯一的插件 id
和 version
属性来声明插件,Gradle 需要一种方法来查找插件实现 artifact 的坐标。
为此,Gradle 将查找坐标为 plugin.id:plugin.id.gradle.plugin:plugin.version
的插件标记 Artifact。该标记需要依赖于实际的插件实现。发布这些标记由java-gradle-plugin自动化完成。
例如,来自 sample-plugins
项目的以下完整示例展示了如何结合使用java-gradle-plugin、maven-publish插件和ivy-publish插件,将 com.example.hello
插件和 com.example.goodbye
插件发布到 Ivy 和 Maven 仓库。
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"))
}
}
}
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 布局类似)

传统插件应用
随着plugins DSL的引入,用户几乎没有理由使用传统的插件应用方法。此处对其进行说明,以备构建作者因其当前的工作方式限制而无法使用插件 DSL 的情况。
apply(plugin = "java")
apply plugin: 'java'
插件可以使用插件 ID 应用。在上述情况中,我们使用短名称 "java" 来应用 JavaPlugin。
除了使用插件 ID 外,还可以通过简单地指定插件类来应用插件
apply<JavaPlugin>()
apply plugin: JavaPlugin
上述示例中的 JavaPlugin
符号指的是 JavaPlugin。由于 org.gradle.api.plugins
包在所有构建脚本中会自动导入(参见默认导入),因此严格来说不需要导入该类。
此外,在 Kotlin 中需要附加 ::class
后缀来标识类字面值,而不是像 Java 中那样使用 .class
。
此外,在 Groovy 中无需附加 .class
来标识类字面值,而在 Java 中需要。
您可能还会看到使用 apply
方法来包含整个构建文件
apply(from = "other.gradle.kts")
apply from: 'other.gradle'
使用版本目录
当项目使用版本目录时,应用插件时可以通过别名引用。
我们来看一个简单的版本目录
[versions]
groovy = "3.0.5"
checkstyle = "8.37"
[libraries]
groovy-core = { module = "org.codehaus.groovy:groovy", version.ref = "groovy" }
groovy-json = { module = "org.codehaus.groovy:groovy-json", version.ref = "groovy" }
groovy-nio = { module = "org.codehaus.groovy:groovy-nio", version.ref = "groovy" }
commons-lang3 = { group = "org.apache.commons", name = "commons-lang3", version = { strictly = "[3.8, 4.0[", prefer="3.9" } }
[bundles]
groovy = ["groovy-core", "groovy-json", "groovy-nio"]
[plugins]
versions = { id = "com.github.ben-manes.versions", version = "0.45.0" }
然后可以使用 alias
方法将插件应用到任何构建脚本
plugins {
`java-library`
alias(libs.plugins.versions)
}
plugins {
id 'java-library'
alias(libs.plugins.versions)
}
Gradle 为目录项生成类型安全访问器。 |
下一步:学习如何编写插件 >>