有許多不同類型的 Gradle「附加元件」可供開發,例如 外掛工作專案擴充人工製品轉換,這些元件全部都實作為類別和可以在 JVM 上執行的其他類型。本章節討論這些類型共有的部分功能和概念。您可以使用這些功能來協助實作自訂 Gradle 類型,並為您的使用者提供一致的 DSL。

本章節適用於下列類型

  • 外掛類型。

  • 工作類型。

  • 人工製品轉換參數類型。

  • Worker API 工作動作參數類型。

  • 使用 ExtensionContainer.create() 建立的擴充物件,例如由外掛註冊的專案擴充。

  • 使用 ObjectFactory.newInstance() 建立的物件。

  • 為受管理的巢狀屬性建立的物件。

  • NamedDomainObjectContainer 的元素。

使用屬性進行設定

您實作的客製化 Gradle 類型通常會保留一些設定,而您希望讓這些設定可用於建置指令碼和其他外掛程式。例如,下載工作可能會有一些設定,用於指定要下載的 URL 和要將結果寫入的檔案系統位置。

受管理的屬性

Gradle 提供自己的受管理屬性概念,讓您可以將每個屬性宣告為抽象 getter (Java、Groovy) 或抽象屬性 (Kotlin)。然後,Gradle 會自動提供此類屬性的實作。它稱為受管理屬性,因為 Gradle 會負責管理屬性的狀態。屬性可能是可變的,表示它同時具有 get() 方法和 set() 方法,或唯讀的,表示它只有 get() 方法。唯讀的屬性也稱為提供者

可變的受管理屬性

若要宣告可變的受管理屬性,請加入類型為 Property<T> 的抽象 getter 方法,其中 T 可以是任何可序列化類型或完全的 Gradle 受管理類型。(請參閱下方清單,以取得更具體的屬性類型。) 屬性不應有任何 setter 方法。以下是具有類型為 URIuri 屬性的工作類型範例

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>

  • RegularFileProperty

  • DirectoryProperty

  • ListProperty<T>

  • SetProperty<T>

  • MapProperty<K, V>

  • ConfigurableFileCollection

  • ConfigurableFileTree

  • DomainObjectSet<T>

  • NamedDomainObjectContainer<T>

  • ExtensiblePolymorphicDomainObjectContainer<T>

  • DependencyCollector

Gradle 建立受管理屬性的值的方式與 ObjectFactory 相同。

唯讀的受管理屬性

若要宣告唯讀的受管理屬性,也稱為提供者,請加入類型為 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)
    }
}

唯讀的受管理巢狀屬性

若要宣告唯讀管理巢狀屬性,請將屬性的抽象 getter 方法新增至使用 @Nested 標註的類型。屬性不應有任何 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();
}

請注意,屬性若要被視為唯讀管理巢狀屬性,屬性的 getter 方法必須為 abstract,且具有 publicprotected 可見性。屬性不應有任何 setter 方法。此外,屬性 getter 必須使用 @Nested 標註。

唯讀管理「名稱」屬性

如果類型包含一個稱為「名稱」的抽象屬性,類型為 String,Gradle 會提供 getter 方法的實作,並在每個建構函式中延伸一個「名稱」參數,此參數會出現在所有其他建構函式參數之前。如果類型是介面,Gradle 會提供一個具有單一「名稱」參數和 @Inject 語意的建構函式。

您可以讓您的類型實作或延伸 Named 介面,此介面定義此類唯讀「名稱」屬性。

管理類型

管理類型是沒有欄位的抽象類別或介面,其屬性都是管理的。也就是說,它是一個其狀態完全由 Gradle 管理的類型。

命名管理類型是管理類型,另外還具有類型為 String 的抽象屬性「名稱」。命名管理類型特別適用於 NamedDomainObjectContainer 的元素類型(請參閱下方)。

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

Java bean 屬性。

有時候您可能會看到以 Java bean 屬性樣式實作的屬性。也就是說,它們不使用 Property<T>Provider<T> 類型,而是使用具體的 setter 和 getter 方法(或 Groovy 或 Kotlin 中對應的便利方法)來實作。這種屬性定義樣式在 Gradle 中已過時,不建議使用。Gradle 核心外掛中仍然使用這種樣式的屬性將在未來版本中移轉到管理屬性。

DSL 支援和可擴充性

當 Gradle 建立自訂類型的執行個體時,它會「裝飾」該執行個體以混合 DSL 和可擴充性支援。

每個裝飾的執行個體都實作 ExtensionAware,因此可以附加擴充物件。

請注意,由於向後相容性問題,目前不會裝飾使用 Project.container() 建立的外掛和容器元素。

服務注入

Gradle 提供許多自訂 Gradle 類型可使用的有用服務。例如,任務可以使用 WorkerExecutor 服務並行執行工作,如 工作人員 API 區段中所見。服務是透過「服務注入」提供的。

可注入的服務

以下服務可供注入

在上述服務中,ProjectLayoutWorkerExecutor 服務僅可注入至專案外掛中。

建構函式注入

物件有兩種方式可以接收它需要的服務。第一個選項是將服務新增為類別建構函式的參數。建構函式必須加上 javax.inject.Inject 註解。Gradle 使用每個建構函式參數的宣告類型來判斷物件需要的服務。建構函式參數的順序及其名稱並不重要,可以隨意設定。

以下範例顯示透過建構函式接收 ObjectFactory 的任務類型

Download.java
public class Download extends DefaultTask {
    private final DirectoryProperty outputDirectory;

    // Inject an ObjectFactory into the constructor
    @Inject
    public Download(ObjectFactory objectFactory) {
        // Use the factory
        outputDirectory = objectFactory.directoryProperty();
    }

    @OutputDirectory
    public DirectoryProperty getOutputDirectory() {
        return outputDirectory;
    }

    @TaskAction
    void run() {
        // ...
    }
}

屬性注入

或者,可以透過將加上 javax.inject.Inject 註解的屬性 getter 方法新增至類別來注入服務。這在例如由於向後相容性限制而無法變更類別的建構函式時會很有用。這個模式也允許 Gradle 延後建立服務,直到呼叫 getter 方法時才建立,而不是在建立執行個體時建立。這有助於提升效能。Gradle 使用 getter 方法的宣告回傳類型來判斷要提供的服務。屬性的名稱並不重要,可以隨意設定。

屬性 getter 方法必須是 publicprotected。方法可以是 abstract,或在無法使用此方法時,可以有虛擬方法主體。方法主體會被捨棄。

以下範例顯示透過屬性 getter 方法接收兩個服務的任務類型

Download.java
public abstract class Download extends DefaultTask {
    // Use an abstract getter method
    @Inject
    protected abstract ObjectFactory getObjectFactory();

    // Alternatively, use a getter method with a dummy implementation
    @Inject
    protected WorkerExecutor getWorkerExecutor() {
        // Method body is ignored
        throw new UnsupportedOperationException();
    }

    @TaskAction
    void run() {
        WorkerExecutor workerExecutor = getWorkerExecutor();
        ObjectFactory objectFactory = getObjectFactory();
        // Use the executor and factory ...
    }
}

明確建立物件

優先使用受管理屬性讓 Gradle 自動建立物件。

自訂 Gradle 類型可以使用ObjectFactory服務建立 Gradle 類型的執行個體,以用於其屬性值。這些執行個體可以使用本章節討論的功能,讓您可以建立物件和巢狀 DSL。

在以下範例中,專案延伸模組透過建構函式接收 ObjectFactory 實例。建構函式使用此實例來建立巢狀 Resource 物件(也是自訂 Gradle 類型),並透過 resource 屬性提供此物件。

DownloadExtension.java
public class DownloadExtension {
    // A nested instance
    private final Resource resource;

    @Inject
    public DownloadExtension(ObjectFactory objectFactory) {
        // Use an injected ObjectFactory to create a Resource object
        resource = objectFactory.newInstance(Resource.class);
    }

    public Resource getResource() {
        return resource;
    }
}

public interface Resource {
    Property<URI> getUri();
}

集合類型

Gradle 提供類型來維護物件集合,旨在順利延伸 Gradle 的 DSL,並提供延遲組態等實用功能。

NamedDomainObjectContainer

NamedDomainObjectContainer 管理一組物件,其中每個元素都有與之關聯的名稱。容器負責建立和組態元素,並提供建置指令碼可用的 DSL 來定義和組態元素。其目的是容納本身可組態的物件,例如一組自訂 Gradle 物件。

Gradle 在整個 API 中廣泛使用 NamedDomainObjectContainer 類型。例如,用於管理專案任務的 project.tasks 物件是 NamedDomainObjectContainer<Task>

您可以使用提供 ObjectFactory.domainObjectContainer() 方法的 ObjectFactory 服務來建立容器實例。也可以使用 Project.container() 方法來使用此方法,不過在自訂 Gradle 類型中,通常最好使用注入的 ObjectFactory 服務,而不是傳遞 Project 實例。

您也可以使用上述所述的 唯讀受管理屬性 來建立容器實例。

若要使用任何 domainObjectContainer() 方法的類型,它必須

  • 命名受管理類型;或

  • 公開一個名為「name」的屬性,作為物件的唯一且不變的名稱。此方法的 domainObjectContainer(Class) 變體會透過呼叫類別的建構函式來建立新實例,該建構函式會採用字串引數,也就是物件的所需名稱。

以這種方式建立的物件會被視為自訂的 Gradle 類型,因此可以使用本章中討論到的功能,例如服務注入或受管理的屬性。

請參閱上述連結,以取得允許自訂實例化策略的 domainObjectContainer() 方法變數。

DownloadExtension.java
public interface DownloadExtension {
    NamedDomainObjectContainer<Resource> getResources();
}

public interface Resource {
    // Type must have a read-only 'name' property
    String getName();

    Property<URI> getUri();

    Property<String> getUserName();
}

對於每個容器屬性,Gradle 會自動將區塊新增至 Groovy 和 Kotlin DSL,您可以使用它來設定容器的內容

範例 9. 設定區塊
build.gradle.kts
plugins {
    id("org.gradle.sample.download")
}

download {
    // Can use a block to configure the container contents
    resources {
        register("gradle") {
            uri = uri("https://gradle.org")
        }
    }
}
build.gradle
plugins {
    id("org.gradle.sample.download")
}

download {
    // Can use a block to configure the container contents
    resources {
        register('gradle') {
            uri = uri('https://gradle.org')
        }
    }
}

ExtensiblePolymorphicDomainObjectContainer

ExtensiblePolymorphicDomainObjectContainerNamedDomainObjectContainer,允許您為不同類型的物件定義實例化策略。

您可以使用 ObjectFactory.polymorphicDomainObjectContainer() 方法建立一個實例。

NamedDomainObjectSet

NamedDomainObjectSet 包含一組可設定的物件,其中每個元素都有與之關聯的名稱。這類似於 NamedDomainObjectContainer,不過 NamedDomainObjectSet 不會管理集合中的物件。它們需要手動建立和新增。

您可以使用 ObjectFactory.namedDomainObjectSet() 方法建立一個實例。

NamedDomainObjectList

NamedDomainObjectList 包含一組可設定的物件,其中每個元素都有與之關聯的名稱。這類似於 NamedDomainObjectContainer,不過 NamedDomainObjectList 不會管理集合中的物件。它們需要手動建立和新增。

您可以使用 ObjectFactory.namedDomainObjectList() 方法建立一個實例。

DomainObjectSet

DomainObjectSet 僅包含一組可設定的物件。與 NamedDomainObjectContainer 相比,DomainObjectSet 不會管理集合中的物件。它們需要手動建立和新增。

您可以使用 ObjectFactory.domainObjectSet() 方法建立一個實例。