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

prop prov 1

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.set(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,覆盖可能存在的任何值。这允许您在配置值之前将 ProviderProperty 实例连接在一起。

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

理解提供者

提供者是表示可能不会立即可用的值的对象。提供者对于延迟求值非常有用,并且可以用于建模可能随时间变化或依赖于其他 task 或输入的值

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.map(Transformer) 从另一个 Provider 创建 Provider

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

使用 Gradle 管理的属性

Gradle 的管理属性允许您将属性声明为抽象 getter(Java、Groovy)或抽象属性(Kotlin)。

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

属性可以是可变的,这意味着它同时具有 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> messageProperty = project.objects.property(String)

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

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

或者只读的,这意味着它只有 get() 方法。只读属性是提供者

build.gradle.kts
abstract class MyProviderTask : DefaultTask() {
    final 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 方法。

这是一个 task 类型的示例,它具有 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 方法必须是 abstract 并且具有 publicprotected 可见性。

属性类型必须是以下之一

属性类型 注意

Property<T>

其中 T 通常是 DoubleIntegerLongStringBool

RegularFileProperty

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

DirectoryProperty

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

ListProperty<T>

类型为 T 的元素列表

SetProperty<T>

类型为 T 的元素集合

MapProperty<K, V>

具有 V 类型值的 K 类型键的映射

ConfigurableFileCollection

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

ConfigurableFileTree

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

只读的管理属性(提供者)

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

这是一个 task 类型的示例,它具有从 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> 代替。

这是一个 task 类型的示例,它具有 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" 属性(提供者)

如果该类型包含一个名为 "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);
    }
}