Gradle 的许多功能通过插件交付,包括 Gradle 发布的 核心插件、第三方插件以及在构建中定义的脚本插件。
插件引入新的 task(例如,JavaCompile
)、领域对象(例如,SourceSet
)、约定(例如,将 Java 源代码放在 src/main/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
。
插件 DSL 的局限性
插件 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。
以下示例显示了如何将插件实现类 my.MyPlugin
(在 buildSrc
中定义)绑定到 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{}
块中发出的插件请求,例如,更改请求的版本或显式指定实现工件坐标。
要添加解析规则,请在 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 使用指定的插件实现工件,而不是其从插件 ID 到 Maven/Ivy 坐标的内置默认映射。
自定义 Maven 和 Ivy 插件仓库必须包含 插件标记工件 和实现插件的工件。 有关将插件发布到自定义仓库的更多信息,请阅读 Gradle Plugin Development Plugin。
有关使用 pluginManagement{}
块的完整文档,请参阅 PluginManagementSpec。
插件标记工件
由于 plugins{}
DSL 块仅允许通过其全局唯一的插件 id
和 version
属性声明插件,因此 Gradle 需要一种查找插件实现工件坐标的方法。
为此,Gradle 将查找坐标为 plugin.id:plugin.id.gradle.plugin:plugin.version
的插件标记工件。 此标记需要依赖于实际的插件实现。 发布这些标记由 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 中标识类文字时,无需像 Java 中那样附加 .class
。
您可能还会看到使用 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 为目录项生成类型安全访问器。 |
下一步: 学习如何编写插件 >>