Gradle 提供了对延迟配置很重要的属性。在实现自定义任务或插件时,必须使用这些延迟属性。

Gradle 使用两个接口表示延迟属性

  1. Property - 表示一个可以查询和更改的值。

  2. Provider - 表示一个只能查询而不能更改的值。

属性和提供者管理构建脚本中的_值和配置_。

在这个例子中,`configuration` 是一个 `Property<String>`,它被设置为 `configurationProvider` `Provider<String>`。`configurationProvider` 延迟提供值 `"Hello, Gradle!"`

build.gradle.kts
abstract class MyIntroTask : DefaultTask() {
    @get:Input
    abstract val configuration: Property<String>

    @TaskAction
    fun printConfiguration() {
        println("Configuration value: ${configuration.get()}")
    }
}

val configurationProvider: Provider<String> = project.provider { "Hello, Gradle!" }

tasks.register("myIntroTask", MyIntroTask::class) {
    configuration = configurationProvider
}
build.gradle
abstract class MyIntroTask extends DefaultTask {
    @Input
    abstract Property<String> getConfiguration()

    @TaskAction
    void printConfiguration() {
        println "Configuration value: ${configuration.get()}"
    }
}

Provider<String> configurationProvider = project.provider { "Hello, Gradle!" }

tasks.register("myIntroTask", MyIntroTask) {
    it.setConfiguration(configurationProvider)
}

理解属性

Gradle 中的属性是保存值的变量。它们可以在构建脚本中定义和访问,以存储文件路径、版本号或自定义值等信息。

属性可以使用 `project` 对象进行设置和检索

build.gradle.kts
// Setting a property
val simpleMessageProperty: Property<String> = project.objects.property(String::class)
simpleMessageProperty.set("Hello, World from a Property!")
// Accessing a property
println(simpleMessageProperty.get())
build.gradle
// Setting a property
def simpleMessageProperty = project.objects.property(String)
simpleMessageProperty.set("Hello, World from a Property!")
// Accessing a property
println(simpleMessageProperty.get())

属性

  • 这些类型的属性是可配置的。

  • `Property` 扩展了 `Provider` 接口。

  • 方法 Property.set(T) 为属性指定一个值,覆盖可能存在的任何值。

  • 方法 Property.set(Provider) 为属性指定一个 `Provider` 的值,覆盖可能存在的任何值。这允许你在值配置之前将 `Provider` 和 `Property` 实例连接起来。

  • 一个 `Property` 可以通过工厂方法 ObjectFactory.property(Class) 创建。

理解提供者

提供者是表示可能无法立即获得的值的对象。提供者对于延迟评估很有用,可以用于建模可能随时间变化或依赖于其他任务或输入的值。

build.gradle.kts
// Setting a provider
val simpleMessageProvider: Provider<String> = project.providers.provider { "Hello, World from a Provider!" }
// Accessing a provider
println(simpleMessageProvider.get())
build.gradle
// Setting a provider
def simpleMessageProvider = project.providers.provider { "Hello, World from a Provider!" }
// Accessing a provider
println(simpleMessageProvider.get())

提供者

  • 这些类型的属性是只读的。

  • 方法 Provider.get() 返回属性的当前值。

  • 一个 `Provider` 可以使用 Provider.map(Transformer) 从另一个 `Provider` 创建。

  • 许多其他类型扩展了 `Provider`,可以在需要 `Provider` 的任何地方使用。

使用 Gradle 托管属性

Gradle 的托管属性允许你将属性声明为 getter(Java、Groovy)或属性(Kotlin)。

Gradle 然后自动为这些属性提供实现,管理它们的状态。

一个属性可以是_可变的_,这意味着它是 `Property` 类型,它同时具有 `get()` 和 `set()` 方法

build.gradle.kts
abstract class MyPropertyTask : DefaultTask() {
    @get:Input
    abstract val messageProperty: Property<String> // message property

    @TaskAction
    fun printMessage() {
        println(messageProperty.get())
    }
}

tasks.register<MyPropertyTask>("myPropertyTask") {
    messageProperty.set("Hello, Gradle!")
}
build.gradle
abstract class MyPropertyTask extends DefaultTask {
    @Input
    abstract Property<String> getMessageProperty()

    @TaskAction
    void printMessage() {
        println(messageProperty.get())
    }
}

tasks.register('myPropertyTask', MyPropertyTask) {
    messageProperty.set("Hello, Gradle!")
}

或者_只读_,这意味着它是 `Provider` 类型,它只有 `get()` 方法

build.gradle.kts
abstract class MyProviderTask : DefaultTask() {
    val messageProvider: Provider<String> = project.providers.provider { "Hello, Gradle!" } // message provider

    @TaskAction
    fun printMessage() {
        println(messageProvider.get())
    }
}

tasks.register<MyProviderTask>("MyProviderTask") {

}
build.gradle
abstract class MyProviderTask extends DefaultTask {
    final Provider<String> messageProvider = project.providers.provider { "Hello, Gradle!" }

    @TaskAction
    void printMessage() {
        println(messageProvider.get())
    }
}

tasks.register('MyProviderTask', MyProviderTask)

可变托管属性

可变托管属性使用 `Property<T>` 类型的 getter 方法声明,其中 `T` 可以是任何可序列化的类型或完全托管的 Gradle 类型。该属性不得有任何 setter 方法。

这是一个任务类型的示例,其中包含类型为 `URI` 的 `uri` 属性

Download.java
public abstract class Download extends DefaultTask {
    @Input
    public abstract Property<URI> getUri(); // abstract getter of type Property<T>

    @TaskAction
    void run() {
        System.out.println("Downloading " + getUri().get()); // Use the `uri` property
    }
}

请注意,要将属性视为可变托管属性,该属性的 getter 方法必须具有 `public` 或 `protected` 可见性。建议也将该属性设为 `abstract`,以便 Gradle 可以管理该属性的初始化。

属性类型必须是以下之一

属性类型 注意

Property<T>

其中 `T` 通常是 `Double`、`Integer`、`Long`、`String` 或 `Bool`

RegularFileProperty

可配置的常规文件位置,其值是可变的

DirectoryProperty

可配置的目录位置,其值是可变的

ListProperty<T>

类型为 `T` 的元素列表

SetProperty<T>

类型为 `T` 的元素集合

MapProperty<K, V>

键类型为 `K`,值类型为 `V` 的 Map

ConfigurableFileCollection

一个可变的 `FileCollection`,表示文件系统位置的集合

ConfigurableFileTree

一个可变的 `FileTree`,表示文件层次结构

只读托管属性 (Providers)

你可以使用 `Provider<T>` 类型的 getter 方法声明只读托管属性,也称为提供者。方法实现需要派生值。例如,它可以从其他属性派生值。

这是一个任务类型的示例,其中包含一个从 `location` 属性派生的 `uri` 提供者

Download.java
public abstract class Download extends DefaultTask {
    @Input
    public abstract Property<String> getLocation();

    @Internal
    public Provider<URI> getUri() {
        return getLocation().map(l -> URI.create("https://" + l));
    }

    @TaskAction
    void run() {
        System.out.println("Downloading " + getUri().get());  // Use the `uri` provider (read-only property)
    }
}

只读托管嵌套属性(嵌套提供者)

你可以通过向带有 @Nested 注解的类型添加属性的抽象 getter 方法来声明只读托管嵌套属性。该属性不应有任何 setter 方法。Gradle 为 getter 方法提供实现,并为属性创建一个值。

当自定义类型具有具有相同生命周期的嵌套复杂类型时,此模式很有用。如果生命周期不同,请考虑改用 `Property<NestedType>`。

这是一个任务类型的示例,其中包含一个 `resource` 属性。`Resource` 类型也是一个自定义 Gradle 类型,并定义了一些托管属性

Download.java
public abstract class Download extends DefaultTask {
    @Nested
    public abstract Resource getResource(); // Use an abstract getter method annotated with @Nested

    @TaskAction
    void run() {
        // Use the `resource` property
        System.out.println("Downloading https://" + getResource().getHostName().get() + "/" + getResource().getPath().get());
    }
}

public interface Resource {
    @Input
    Property<String> getHostName();
    @Input
    Property<String> getPath();
}

只读托管“名称”属性(提供者)

如果类型包含一个名为“name”的 `String` 类型抽象属性,Gradle 会为 getter 方法提供一个实现,并扩展每个构造函数,使其包含一个“name”参数,该参数位于所有其他构造函数参数之前。

如果类型是接口,Gradle 将提供一个带有单个“name”参数和 `@Inject` 语义的构造函数。

你可以让你的类型实现或扩展 Named 接口,该接口定义了这样一个只读的“name”属性

import org.gradle.api.Named

interface MyType : Named {
    // Other properties and methods...
}

class MyTypeImpl(override val name: String) : MyType {
    // Implement other properties and methods...
}

// Usage
val instance = MyTypeImpl("myName")
println(instance.name) // Prints: myName

使用 Gradle 托管类型

托管类型是抽象类或接口,没有字段,并且其所有属性都是托管的。这些类型的状态完全由 Gradle 管理。

例如,这个托管类型被定义为一个接口

Resource.java
public interface Resource {
    @Input
    Property<String> getHostName();
    @Input
    Property<String> getPath();
}

_命名托管类型_是一种托管类型,它还具有一个名为“name”的 `String` 类型抽象属性。命名托管类型作为 NamedDomainObjectContainer 的元素类型特别有用

build.gradle.kts
interface MyNamedType {
    val name: String
}

class MyNamedTypeImpl(override val name: String) : MyNamedType

class MyPluginExtension(project: Project) {
    val myNamedContainer: NamedDomainObjectContainer<MyNamedType> =
        project.container(MyNamedType::class.java) { name ->
            project.objects.newInstance(MyNamedTypeImpl::class.java, name)
        }
}
build.gradle
interface MyNamedType {
    String getName()
}

class MyNamedTypeImpl implements MyNamedType {
    String name

    MyNamedTypeImpl(String name) {
        this.name = name
    }
}

class MyPluginExtension {
    NamedDomainObjectContainer<MyNamedType> myNamedContainer

    MyPluginExtension(Project project) {
        myNamedContainer = project.container(MyNamedType) { name ->
            new MyNamedTypeImpl(name)
        }
    }
}

使用 Java Bean 属性

有时你可能会看到以 Java bean 属性样式实现的属性。也就是说,它们不使用 `Property<T>` 或 `Provider<T>` 类型,而是通过具体的 setter 和 getter 方法(或 Groovy 或 Kotlin 中相应的便利方法)实现。

Gradle 不鼓励这种属性定义样式,它是遗留的

public class MyTask extends DefaultTask {
    private String someProperty;

    public String getSomeProperty() {
        return someProperty;
    }

    public void setSomeProperty(String someProperty) {
        this.someProperty = someProperty;
    }

    @TaskAction
    public void myAction() {
        System.out.println("SomeProperty: " + someProperty);
    }
}