理想情况下,Groovy 构建脚本主要看起来像配置:设置项目的一些属性,配置依赖项,声明任务等等。该配置基于 Groovy 语言结构。本入门旨在解释这些结构是什么,以及 — 最重要的是 — 它们与 Gradle 的 API 文档之间的关系。
Project
对象
由于 Groovy 是一种基于 Java 的面向对象语言,因此其属性和方法适用于对象。在某些情况下,对象是隐式的 — 特别是在构建脚本的顶层,即未嵌套在 {}
代码块中。
考虑以下构建脚本片段,其中包含一个非限定属性和代码块
version = '1.0.0.GA'
configurations {
...
}
version
和 configurations {}
都是 org.gradle.api.Project 的一部分。
此示例反映了每个 Groovy 构建脚本都由 Project
的隐式实例支持。如果您看到一个非限定元素并且不知道它在哪里定义,请始终检查 Project
API 文档,以查看它是否来自那里。
避免在构建脚本中使用 Groovy MetaClass 编程技术。Gradle 提供了自己的 API 用于添加动态运行时属性。 使用 Groovy 特定的元编程可能会导致构建在构建之间保留大量内存,最终导致 Gradle 守护进程内存溢出。 |
属性
<obj>.<name> // Get a property value
<obj>.<name> = <value> // Set a property to a new value
"$<name>" // Embed a property value in a string
"${<obj>.<name>}" // Same as previous (embedded value)
version = '1.0.1'
myCopyTask.description = 'Copies some files'
file("$projectDir/src")
println "Destination: ${myCopyTask.destinationDir}"
属性表示对象的某种状态。=
符号的存在清楚地表明您正在查看属性。否则,限定名称 — 它以 <obj>.
开头 — 没有任何其他修饰的也是属性。
如果名称是非限定的,则它可能是以下之一
请注意,插件可以向 Project
对象添加自己的属性。API 文档列出了核心插件添加的所有属性。如果您很难找到属性的来源,请查看构建使用的插件的文档。
在构建脚本中引用由非核心插件添加的项目属性时,请考虑以 project. 为前缀 — 这样可以清楚地表明该属性属于 project 对象。 |
API 文档中的属性
Groovy DSL 参考显示了属性在构建脚本中的使用方式,但 Javadoc 仅显示方法。这是因为属性在幕后作为方法实现
-
如果存在一个名为
get<PropertyName>
的零参数方法,该方法返回与属性相同的类型,则可以读取属性。 -
如果存在一个名为
set<PropertyName>
的单参数方法,该参数具有与属性相同的类型,并且返回类型为void
,则可以修改属性。
请注意,属性名称通常以小写字母开头,但该字母在方法名称中为大写。因此,getter 方法 getProjectVersion()
对应于属性 projectVersion
。当名称以至少两个大写字母开头时,此约定不适用,在这种情况下,大小写没有变化。例如,getRAM()
对应于属性 RAM
。
project.getVersion()
project.version
project.setVersion('1.0.1')
project.version = '1.0.1'
方法
<obj>.<name>() // Method call with no arguments
<obj>.<name>(<arg>, <arg>) // Method call with multiple arguments
<obj>.<name> <arg>, <arg> // Method call with multiple args (no parentheses)
myCopyTask.include '**/*.xml', '**/*.properties'
ext.resourceSpec = copySpec() // `copySpec()` comes from `Project`
file('src/main/java')
println 'Hello, World!'
方法表示对象的某种行为,尽管 Gradle 通常也使用方法来配置对象的状态。方法可以通过其参数或空括号来识别。请注意,有时需要括号,例如当方法没有参数时,因此您可能会发现始终使用括号最简单。
Gradle 有一个约定,如果一个方法与基于集合的属性具有相同的名称,则该方法将其值附加到该集合中。 |
代码块
代码块也是方法,只是最后一个参数具有特定类型。
<obj>.<name> {
...
}
<obj>.<name>(<arg>, <arg>) {
...
}
plugins {
id 'java-library'
}
configurations {
assets
}
sourceSets {
main {
java {
srcDirs = ['src']
}
}
}
dependencies {
implementation project(':util')
}
代码块是一种一次性配置构建元素的多个方面的机制。它们还提供了一种嵌套配置的方式,从而形成结构化数据形式。
您应该理解代码块的两个重要方面
-
它们作为具有特定签名的方法实现。
-
它们可以更改非限定方法和属性的目标(“委托”)。
两者都基于 Groovy 语言特性,我们在以下部分中解释它们。
代码块方法签名
您可以通过方法的签名,更具体地说,是其参数类型,轻松地将方法标识为代码块背后的实现。如果方法对应于代码块
-
它必须至少有一个参数。
-
最后一个参数必须是
groovy.lang.Closure
或 org.gradle.api.Action 类型。
例如,Project.copy(Action) 符合这些要求,因此您可以使用以下语法
copy {
into layout.buildDirectory.dir("tmp")
from 'custom-resources'
}
这就引出了 into()
和 from()
如何工作的问题。它们显然是方法,但是您会在 API 文档的哪里找到它们?答案来自理解对象委托。
委托
关于属性的部分列出了可以在哪里找到非限定属性。一个常见的位置是在 Project
对象上。但是,在代码块内部,这些非限定属性和方法还有一个替代来源:代码块的委托对象。
为了帮助解释这个概念,请考虑上一节中的最后一个示例
copy {
into layout.buildDirectory.dir("tmp")
from 'custom-resources'
}
此示例中的所有方法和属性都是非限定的。您可以轻松地在Project
API 文档中找到 copy()
和 layout
,但是 into()
和 from()
呢?这些是针对 copy {}
代码块的委托解析的。该委托的类型是什么?您需要检查该 API 文档。
有两种方法可以确定委托类型,具体取决于代码块方法的签名
-
对于
Action
参数,请查看类型的参数。在上面的示例中,方法签名是
copy(Action<? super CopySpec>)
,角度括号内的部分告诉您委托类型 — 在本例中为 CopySpec。 -
对于
Closure
参数,文档将在描述中明确说明正在配置的类型或委托的类型(相同事物的不同术语)。
所有新的 Gradle API 都声明 Action
参数类型而不是 Closure
,这使得选择委托类型非常容易。即使是较旧的 API 除了旧的 Closure
变体之外,也具有 Action
变体。