理想情况下,Groovy 构建脚本看起来大多像配置:设置项目的一些属性、配置依赖项、声明任务等等。该配置基于 Groovy 语言构造。本入门书旨在解释这些构造是什么,以及最重要的是它们与 Gradle 的 API 文档的关系。

物体Project

由于 Groovy 是一种基于 Java 的面向对象语言,因此它的属性和方法适用于对象。在某些情况下,对象是隐式的——特别是在构建脚本的顶层,即不嵌套在块内{}

考虑构建脚本的这个片段,其中包含不合格的属性和块:

version = '1.0.0.GA'

configurations {
    ...
}

version都是org.gradle.api.Projectconfigurations {}的一部分。

此示例反映了每个 Groovy 构建脚本如何由Project.如果您看到不合格的元素并且不知道它是在哪里定义的,请务必检查ProjectAPI 文档以查看它是否来自该元素。

避免在构建脚本中使用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上的财产。

  • 在项目其他地方定义的额外属性

  • 内隐式对象的属性。

  • 先前在构建脚本中定义的局部变量

请注意,插件可以将自己的属性添加到对象中Project。 API文档列出了核心插件添加的所有属性。如果您很难找到属性的来源,请检查构建使用的插件的文档。

当在构建脚本中引用由非核心插件添加的项目属性时,请考虑为其添加前缀project.- 很明显该属性属于项目对象。

API 文档中的属性

Groovy DSL 参考显示了构建脚本中使用的属性,但 Javadoc 仅显示方法。这是因为属性是作为幕后方法实现的:

  • 如果有一个以零参数命名的方法返回与该属性相同的类型,则可以读取该属性。get<PropertyName>

  • 如果存在一个以一个参数命名的方法,该方法与该属性具有相同类型且返回类型为,则可以修改该属性。set<PropertyName>void

请注意,属性名称通常以小写字母开头,但该字母在方法名称中为大写字母。所以getter方法getProjectVersion()对应的是property 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')
}

块是一种一次性配置构建元素多个方面的机制。它们还提供了一种嵌套配置的方法,从而产生结构化数据的形式。

您应该了解块的两个重要方面:

  1. 它们被实现为具有特定签名的方法。

  2. 他们可以更改不合格方法和属性的目标(“委托”)。

两者都基于 Groovy 语言功能,我们将在以下部分中对其进行解释。

块方法签名

您可以通过其签名(或更具体地说,其参数类型)轻松地将方法识别为块背后的实现。如果一个方法对应一个块:

例如,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'
}

此示例中的所有方法和属性都是不合格的。你可以很容易地在API 文档copy()中找到and ,但是and呢?这些是针对该块的代表来解决的。该代表的类型是什么?您需要检查该 API 文档layoutProjectinto()from()copy {}

有两种方法可以确定委托类型,具体取决于块方法的签名:

  • 对于Action参数,请查看类型的参数。

    在上面的示例中,方法签名是copy(Action<? super CopySpec>),尖括号内的位告诉您委托类型 - 在本例中为CopySpec 。

  • 对于Closure参数,文档将在描述中明确说明正在配置的类型或委托的类型(同一事物的不同术语)。

因此,您可以在 上找到into()from()CopySpec。您甚至可能会注意到,这两个方法都有以 anAction作为最后一个参数的变体,这意味着您可以对它们使用块语法。

所有新的 Gradle API 都声明Action参数类型而不是Closure,这使得选择委托类型变得非常容易。即使是较旧的 API,Action除了旧 API 之外还有一种变体Closure

局部变量

def <name> = <value>        // Untyped variable
<type> <name> = <value>     // Typed variable
例子
def i = 1
String errorMsg = 'Failed, because reasons'

局部变量是 Groovy 构造(与额外属性不同),可用于在构建脚本中共享值。

避免在项目根目录中使用局部变量,即作为伪项目属性。它们无法在构建脚本之外读取,Gradle 也不了解它们。

在较小的上下文中(例如配置任务),局部变量有时会有所帮助。