Gradle 提供了許多有用的服務,可供自訂 Gradle 類型使用。例如,WorkerExecutor 服務可供任務用於平行執行工作,如worker API 章節中所見。這些服務透過服務注入提供。

可用的服務

以下服務可用於注入

  1. ObjectFactory - 允許建立模型物件。

  2. ProjectLayout - 提供對專案主要位置的存取。

  3. BuildLayout - 提供對 Gradle 建置的重要位置的存取。

  4. ProviderFactory - 建立 Provider 實例。

  5. WorkerExecutor - 允許任務平行執行工作。

  6. FileSystemOperations - 允許任務對檔案系統執行操作,例如刪除檔案、複製檔案或同步目錄。

  7. ArchiveOperations - 允許任務對封存檔案執行操作,例如 ZIP 或 TAR 檔案。

  8. ExecOperations - 允許任務執行外部程序,並針對執行外部 java 程式提供專用支援。

  9. ToolingModelBuilderRegistry - 允許外掛程式註冊 Gradle Tooling API 模型。

  10. TestEventReporterFactory - 允許外掛程式存取 Gradle 的測試事件及其對應的 API。

在上述服務中,ProjectLayoutWorkerExecutor 服務僅適用於專案外掛程式中的注入。BuildLayout 僅適用於設定外掛程式和設定檔。ProjectLayout 在 Worker API 動作中不可用。

雖然其他服務在技術上可以注入,但不支援這種做法,且未來可能會導致重大變更。請僅注入上面明確列出的服務,以確保穩定性和相容性。

您應避免注入上面未列出的類型。

1. ObjectFactory

ObjectFactory 是一項用於建立自訂 Gradle 類型的服務,可讓您在建置邏輯中定義巢狀物件和 DSL。它提供用於建立不同類型實例的方法,例如屬性 (Property<T>)、集合 (ListProperty<T>SetProperty<T>MapProperty<K, V>)、檔案相關物件 (RegularFilePropertyDirectoryPropertyConfigurableFileCollectionConfigurableFileTree) 等。

您可以使用 project.objects 屬性取得 ObjectFactory 的實例。以下是一個簡單的範例,示範如何使用 ObjectFactory 建立屬性並設定其值

build.gradle.kts
tasks.register("myObjectFactoryTask") {
    doLast {
        val objectFactory = project.objects
        val myProperty = objectFactory.property(String::class)
        myProperty.set("Hello, Gradle!")
        println(myProperty.get())
    }
}
build.gradle
tasks.register("myObjectFactoryTask") {
    doLast {
        def objectFactory = project.objects
        def myProperty = objectFactory.property(String)
        myProperty.set("Hello, Gradle!")
        println myProperty.get()
    }
}
最好讓 Gradle 使用受管屬性自動建立物件。

使用 ObjectFactory 建立這些物件可確保它們由 Gradle 正確管理,尤其是在組態避免和延遲評估方面。這表示這些物件的值僅在需要時才計算,這可以提高建置效能。

在以下範例中,名為 DownloadExtension 的專案擴充功能透過其建構子接收 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();
}

以下是另一個使用 javax.inject.Inject 的範例

build.gradle.kts
abstract class MyObjectFactoryTask
@Inject constructor(private var objectFactory: ObjectFactory) : DefaultTask() {

    @TaskAction
    fun doTaskAction() {
        val outputDirectory = objectFactory.directoryProperty()
        outputDirectory.convention(project.layout.projectDirectory)
        println(outputDirectory.get())
    }
}

tasks.register("myInjectedObjectFactoryTask", MyObjectFactoryTask::class) {}
build.gradle
abstract class MyObjectFactoryTask extends DefaultTask {
    private ObjectFactory objectFactory

    @Inject //@javax.inject.Inject
    MyObjectFactoryTask(ObjectFactory objectFactory) {
        this.objectFactory = objectFactory
    }

    @TaskAction
    void doTaskAction() {
        var outputDirectory = objectFactory.directoryProperty()
        outputDirectory.convention(project.layout.projectDirectory)
        println(outputDirectory.get())
    }
}

tasks.register("myInjectedObjectFactoryTask",MyObjectFactoryTask) {}

MyObjectFactoryTask 任務使用 ObjectFactory 實例,該實例使用 @Inject 註解注入到任務的建構子中。

2. ProjectLayout

ProjectLayout 是一項服務,提供對 Gradle 專案目錄和檔案配置的存取。它是 org.gradle.api.file 套件的一部分,可讓您查詢專案的配置,以取得關於來源集、建置目錄和專案其他檔案相關方面的資訊。

您可以使用 project.layout 屬性從 Project 物件取得 ProjectLayout 實例。以下是一個簡單的範例

build.gradle.kts
tasks.register("showLayout") {
    doLast {
        val layout = project.layout
        println("Project Directory: ${layout.projectDirectory}")
        println("Build Directory: ${layout.buildDirectory.get()}")
    }
}
build.gradle
tasks.register('showLayout') {
    doLast {
        def layout = project.layout
        println "Project Directory: ${layout.projectDirectory}"
        println "Build Directory: ${layout.buildDirectory.get()}"
    }
}

以下是一個使用 javax.inject.Inject 的範例

build.gradle.kts
abstract class MyProjectLayoutTask
@Inject constructor(private var projectLayout: ProjectLayout) : DefaultTask() {

    @TaskAction
    fun doTaskAction() {
        val outputDirectory = projectLayout.projectDirectory
        println(outputDirectory)
    }
}

tasks.register("myInjectedProjectLayoutTask", MyProjectLayoutTask::class) {}
build.gradle
abstract class MyProjectLayoutTask extends DefaultTask {
    private ProjectLayout projectLayout

    @Inject //@javax.inject.Inject
    MyProjectLayoutTask(ProjectLayout projectLayout) {
        this.projectLayout = projectLayout
    }

    @TaskAction
    void doTaskAction() {
        var outputDirectory = projectLayout.projectDirectory
        println(outputDirectory)
    }
}

tasks.register("myInjectedProjectLayoutTask",MyProjectLayoutTask) {}

MyProjectLayoutTask 任務使用 ProjectLayout 實例,該實例使用 @Inject 註解注入到任務的建構子中。

3. BuildLayout

BuildLayout 是一項服務,提供對設定外掛程式或設定腳本中根目錄和設定目錄的存取,它類似於 ProjectLayout。它是 org.gradle.api.file 套件的一部分,用於以延遲計算的值存取標準的建置範圍檔案系統位置。

這些 API 目前正在孵化中,但最終應取代設定中的現有存取器,這些存取器會傳回急切計算的位置
Settings.rootDirSettings.layout.rootDirectory
Settings.settingsDirSettings.layout.settingsDirectory

您可以使用 settings.layout 屬性從 Settings 物件取得 BuildLayout 實例。以下是一個簡單的範例

settings.gradle.kts
println("Root Directory: ${settings.layout.rootDirectory}")
println("Settings Directory: ${settings.layout.settingsDirectory}")
settings.gradle
println "Root Directory: ${settings.layout.rootDirectory}"
println "Settings Directory: ${settings.layout.settingsDirectory}"

以下是一個使用 javax.inject.Inject 的範例

settings.gradle.kts
abstract class MyBuildLayoutPlugin @Inject constructor(private val buildLayout: BuildLayout) : Plugin<Settings> {
    override fun apply(settings: Settings) {
        println(buildLayout.rootDirectory)
    }
}

apply<MyBuildLayoutPlugin>()
settings.gradle
abstract class MyBuildLayoutPlugin implements Plugin<Settings> {
    private BuildLayout buildLayout

    @Inject //@javax.inject.Inject
    MyBuildLayoutPlugin(BuildLayout buildLayout) {
        this.buildLayout = buildLayout
    }

    @Override void apply(Settings settings) {
        // the meat and potatoes of the plugin
        println buildLayout.rootDirectory
    }
}

apply plugin: MyBuildLayoutPlugin

此程式碼定義了一個 MyBuildLayoutPlugin 外掛程式,該外掛程式實作了 Settings 類型的 Plugin 介面。此外掛程式預期將 BuildLayout 實例使用 @Inject 註解注入到其建構子中。

4. ProviderFactory

ProviderFactory 是一項服務,提供用於建立不同類型提供者的方法。提供者用於為建置腳本中可能延遲計算的值建模。

ProviderFactory 介面提供用於建立各種提供者類型的方法,包括

  • provider(Callable<T> value),用於建立具有值的提供者,該值根據 Callable 延遲計算。

  • provider(Provider<T> value),用於建立僅包裝現有提供者的提供者。

  • property(Class<T> type),用於為特定類型建立屬性提供者。

  • gradleProperty(Class<T> type),用於建立從 Gradle 專案屬性讀取其值的屬性提供者。

以下是一個簡單的範例,示範如何使用 ProviderFactory,使用 project.providers

build.gradle.kts
tasks.register("printMessage") {
    doLast {
        val providerFactory = project.providers
        val messageProvider = providerFactory.provider { "Hello, Gradle!" }
        println(messageProvider.get())
    }
}
build.gradle
tasks.register('printMessage') {
    doLast {
        def providerFactory = project.providers
        def messageProvider = providerFactory.provider { "Hello, Gradle!" }
        println messageProvider.get()
    }
}

名為 printMessage 的任務使用 ProviderFactory 建立提供訊息字串的 provider

以下是一個使用 javax.inject.Inject 的範例

build.gradle.kts
abstract class MyProviderFactoryTask
@Inject constructor(private var providerFactory: ProviderFactory) : DefaultTask() {

    @TaskAction
    fun doTaskAction() {
        val outputDirectory = providerFactory.provider { "build/my-file.txt" }
        println(outputDirectory.get())
    }
}

tasks.register("myInjectedProviderFactoryTask", MyProviderFactoryTask::class) {}
build.gradle
abstract class MyProviderFactoryTask extends DefaultTask {
    private ProviderFactory providerFactory

    @Inject //@javax.inject.Inject
    MyProviderFactoryTask(ProviderFactory providerFactory) {
        this.providerFactory = providerFactory
    }

    @TaskAction
    void doTaskAction() {
        var outputDirectory = providerFactory.provider { "build/my-file.txt" }
        println(outputDirectory.get())
    }
}

tasks.register("myInjectedProviderFactoryTask",MyProviderFactoryTask) {}

ProviderFactory 服務使用 @Inject 註解注入到 MyProviderFactoryTask 任務的建構子中。

5. WorkerExecutor

WorkerExecutor 是一項服務,可讓您使用 Worker 程序執行任務的平行執行。這對於執行 CPU 密集型或長時間執行的操作的任務特別有用,因為它允許它們平行執行,從而提高建置效能。

使用 WorkerExecutor,您可以提交工作單元(稱為動作)以在個別 Worker 程序中執行。這有助於將工作與主要的 Gradle 程序隔離,從而提供更好的可靠性和效能。

以下是如何在建置腳本中使用 WorkerExecutor 的基本範例

build.gradle.kts
abstract class MyWorkAction : WorkAction<WorkParameters.None> {
    private val greeting: String = "Hello from a Worker!"

    override fun execute() {
        println(greeting)
    }
}

abstract class MyWorkerTask
@Inject constructor(private var workerExecutor: WorkerExecutor) : DefaultTask() {
    @get:Input
    abstract val booleanFlag: Property<Boolean>
    @TaskAction
    fun doThings() {
        workerExecutor.noIsolation().submit(MyWorkAction::class.java) {}
    }
}

tasks.register("myWorkTask", MyWorkerTask::class) {}
build.gradle
abstract class MyWorkAction implements WorkAction<WorkParameters.None> {
    private final String greeting;

    @Inject
    public MyWorkAction() {
        this.greeting = "Hello from a Worker!";
    }

    @Override
    public void execute() {
        System.out.println(greeting);
    }
}

abstract class MyWorkerTask extends DefaultTask {
    @Input
    abstract Property<Boolean> getBooleanFlag()

    @Inject
    abstract WorkerExecutor getWorkerExecutor()

    @TaskAction
    void doThings() {
        workerExecutor.noIsolation().submit(MyWorkAction) {}
    }
}

tasks.register("myWorkTask", MyWorkerTask) {}

如需更多詳細資訊,請參閱worker API

6. FileSystemOperations

FileSystemOperations 是一項服務,提供用於執行檔案系統操作(例如複製、刪除和同步)的方法。它是 org.gradle.api.file 套件的一部分,通常用於自訂任務或外掛程式中,以與檔案系統互動。

以下是一個使用 javax.inject.Inject 的範例

build.gradle.kts
abstract class MyFileSystemOperationsTask
@Inject constructor(private var fileSystemOperations: FileSystemOperations) : DefaultTask() {

    @TaskAction
    fun doTaskAction() {
        fileSystemOperations.sync {
            from("src")
            into("dest")
        }
    }
}

tasks.register("myInjectedFileSystemOperationsTask", MyFileSystemOperationsTask::class)
build.gradle
abstract class MyFileSystemOperationsTask extends DefaultTask {
    private FileSystemOperations fileSystemOperations

    @Inject //@javax.inject.Inject
    MyFileSystemOperationsTask(FileSystemOperations fileSystemOperations) {
        this.fileSystemOperations = fileSystemOperations
    }

    @TaskAction
    void doTaskAction() {
        fileSystemOperations.sync {
            from 'src'
            into 'dest'
        }
    }
}

tasks.register("myInjectedFileSystemOperationsTask", MyFileSystemOperationsTask)

FileSystemOperations 服務使用 @Inject 註解注入到 MyFileSystemOperationsTask 任務的建構子中。

透過一些儀式,可以在建置腳本中定義的 Ad Hoc 任務中使用 FileSystemOperations

build.gradle.kts
interface InjectedFsOps {
    @get:Inject val fs: FileSystemOperations
}

tasks.register("myAdHocFileSystemOperationsTask") {
    val injected = project.objects.newInstance<InjectedFsOps>()
    doLast {
        injected.fs.copy {
            from("src")
            into("dest")
        }
    }
}
build.gradle
interface InjectedFsOps {
    @Inject //@javax.inject.Inject
    FileSystemOperations getFs()
}

tasks.register('myAdHocFileSystemOperationsTask') {
    def injected = project.objects.newInstance(InjectedFsOps)
    doLast {
        injected.fs.copy {
            from 'source'
            into 'destination'
        }
    }
}

首先,您需要宣告一個具有 FileSystemOperations 類型屬性的介面(此處命名為 InjectedFsOps),以作為注入點。然後呼叫方法 ObjectFactory.newInstance 以產生持有注入服務的介面實作。

現在是考慮將 Ad Hoc 任務提取到適當類別中的好時機。

7. ArchiveOperations

ArchiveOperations 是一項服務,提供用於存取封存檔案(例如 ZIP 和 TAR 檔案)內容的方法。它是 org.gradle.api.file 套件的一部分,通常用於自訂任務或外掛程式中,以解壓縮封存檔案。

以下是一個使用 javax.inject.Inject 的範例

build.gradle.kts
abstract class MyArchiveOperationsTask
@Inject constructor(
    private val archiveOperations: ArchiveOperations,
    private val layout: ProjectLayout,
    private val fs: FileSystemOperations
) : DefaultTask() {
    @TaskAction
    fun doTaskAction() {
        fs.sync {
            from(archiveOperations.zipTree(layout.projectDirectory.file("sources.jar")))
            into(layout.buildDirectory.dir("unpacked-sources"))
        }
    }
}

tasks.register("myInjectedArchiveOperationsTask", MyArchiveOperationsTask::class)
build.gradle
abstract class MyArchiveOperationsTask extends DefaultTask {
    private ArchiveOperations archiveOperations
    private ProjectLayout layout
    private FileSystemOperations fs

    @Inject
    MyArchiveOperationsTask(ArchiveOperations archiveOperations, ProjectLayout layout, FileSystemOperations fs) {
        this.archiveOperations = archiveOperations
        this.layout = layout
        this.fs = fs
    }

    @TaskAction
    void doTaskAction() {
        fs.sync {
            from(archiveOperations.zipTree(layout.projectDirectory.file("sources.jar")))
            into(layout.buildDirectory.dir("unpacked-sources"))
        }
    }
}

tasks.register("myInjectedArchiveOperationsTask", MyArchiveOperationsTask)

ArchiveOperations 服務使用 @Inject 註解注入到 MyArchiveOperationsTask 任務的建構子中。

透過一些儀式,可以在建置腳本中定義的 Ad Hoc 任務中使用 ArchiveOperations

build.gradle.kts
interface InjectedArcOps {
    @get:Inject val arcOps: ArchiveOperations
}

tasks.register("myAdHocArchiveOperationsTask") {
    val injected = project.objects.newInstance<InjectedArcOps>()
    val archiveFile = "${project.projectDir}/sources.jar"
    doLast {
        injected.arcOps.zipTree(archiveFile)
    }
}
build.gradle
interface InjectedArcOps {
    @Inject //@javax.inject.Inject
    ArchiveOperations getArcOps()
}

tasks.register('myAdHocArchiveOperationsTask') {
    def injected = project.objects.newInstance(InjectedArcOps)
    def archiveFile = "${projectDir}/sources.jar"

    doLast {
        injected.arcOps.zipTree(archiveFile)
    }
}

首先,您需要宣告一個具有 ArchiveOperations 類型屬性的介面(此處命名為 InjectedArcOps),以作為注入點。然後呼叫方法 ObjectFactory.newInstance 以產生持有注入服務的介面實作。

現在是考慮將 Ad Hoc 任務提取到適當類別中的好時機。

8. ExecOperations

ExecOperations 是一項服務,提供用於從建置腳本中執行外部程序(命令)的方法。它是 org.gradle.process 套件的一部分,通常用於自訂任務或外掛程式中,以執行命令列工具或腳本作為建置程序的一部分。

以下是一個使用 javax.inject.Inject 的範例

build.gradle.kts
abstract class MyExecOperationsTask
@Inject constructor(private var execOperations: ExecOperations) : DefaultTask() {

    @TaskAction
    fun doTaskAction() {
        execOperations.exec {
            commandLine("ls", "-la")
        }
    }
}

tasks.register("myInjectedExecOperationsTask", MyExecOperationsTask::class)
build.gradle
abstract class MyExecOperationsTask extends DefaultTask {
    private ExecOperations execOperations

    @Inject //@javax.inject.Inject
    MyExecOperationsTask(ExecOperations execOperations) {
        this.execOperations = execOperations
    }

    @TaskAction
    void doTaskAction() {
        execOperations.exec {
            commandLine 'ls', '-la'
        }
    }
}

tasks.register("myInjectedExecOperationsTask", MyExecOperationsTask)

ExecOperations 使用 @Inject 註解注入到 MyExecOperationsTask 任務的建構子中。

透過一些儀式,可以在建置腳本中定義的 Ad Hoc 任務中使用 ExecOperations

build.gradle.kts
interface InjectedExecOps {
    @get:Inject val execOps: ExecOperations
}

tasks.register("myAdHocExecOperationsTask") {
    val injected = project.objects.newInstance<InjectedExecOps>()

    doLast {
        injected.execOps.exec {
            commandLine("ls", "-la")
        }
    }
}
build.gradle
interface InjectedExecOps {
    @Inject //@javax.inject.Inject
    ExecOperations getExecOps()
}

tasks.register('myAdHocExecOperationsTask') {
    def injected = project.objects.newInstance(InjectedExecOps)

    doLast {
        injected.execOps.exec {
            commandLine 'ls', '-la'
        }
    }
}

首先,您需要宣告一個具有 ExecOperations 類型屬性的介面(此處命名為 InjectedExecOps),以作為注入點。然後呼叫方法 ObjectFactory.newInstance 以產生持有注入服務的介面實作。

現在是考慮將 Ad Hoc 任務提取到適當類別中的好時機。

9. ToolingModelBuilderRegistry

ToolingModelBuilderRegistry 是一項服務,可讓您註冊自訂 Tooling 模型建置器。Tooling 模型用於為 Gradle 專案提供豐富的 IDE 整合,讓 IDE 能夠理解和使用專案的結構、相依性和其他方面。

ToolingModelBuilderRegistry 介面是 org.gradle.tooling.provider.model 套件的一部分,通常用於提供增強 IDE 支援的自訂 Gradle 外掛程式中。

以下是一個簡化的範例

build.gradle.kts
// Implements the ToolingModelBuilder interface.
// This interface is used in Gradle to define custom tooling models that can
// be accessed by IDEs or other tools through the Gradle tooling API.
class OrtModelBuilder : ToolingModelBuilder {
    private val repositories: MutableMap<String, String> = mutableMapOf()

    private val platformCategories: Set<String> = setOf("platform", "enforced-platform")

    private val visitedDependencies: MutableSet<ModuleComponentIdentifier> = mutableSetOf()
    private val visitedProjects: MutableSet<ModuleVersionIdentifier> = mutableSetOf()

    private val logger = Logging.getLogger(OrtModelBuilder::class.java)
    private val errors: MutableList<String> = mutableListOf()
    private val warnings: MutableList<String> = mutableListOf()

    override fun canBuild(modelName: String): Boolean {
        return false
    }

    override fun buildAll(modelName: String, project: Project): Any? {
        return null
    }
}

// Plugin is responsible for registering a custom tooling model builder
// (OrtModelBuilder) with the ToolingModelBuilderRegistry, which allows
// IDEs and other tools to access the custom tooling model.
class OrtModelPlugin(private val registry: ToolingModelBuilderRegistry) : Plugin<Project> {
    override fun apply(project: Project) {
        registry.register(OrtModelBuilder())
    }
}
build.gradle
// Implements the ToolingModelBuilder interface.
// This interface is used in Gradle to define custom tooling models that can
// be accessed by IDEs or other tools through the Gradle tooling API.
class OrtModelBuilder implements ToolingModelBuilder {
    private Map<String, String> repositories = [:]

    private Set<String> platformCategories = ["platform", "enforced-platform"]

    private Set<ModuleComponentIdentifier> visitedDependencies = []
    private Set<ModuleVersionIdentifier> visitedProjects = []

    private static final logger = Logging.getLogger(OrtModelBuilder.class)
    private List<String> errors = []
    private List<String> warnings = []

    @Override
    boolean canBuild(String modelName) {
        return false
    }

    @Override
    Object buildAll(String modelName, Project project) {
        return null
    }
}

// Plugin is responsible for registering a custom tooling model builder
// (OrtModelBuilder) with the ToolingModelBuilderRegistry, which allows
// IDEs and other tools to access the custom tooling model.
class OrtModelPlugin implements Plugin<Project> {
    ToolingModelBuilderRegistry registry

    OrtModelPlugin(ToolingModelBuilderRegistry registry) {
        this.registry = registry
    }

    void apply(Project project) {
        registry.register(new OrtModelBuilder())
    }
}

您可以在Tooling API 了解更多資訊。

10. TestEventReporterFactory

此 API 正在孵化中。

TestEventReporterFactory 是一項服務,提供對測試事件報告 API 的存取。

您可以在測試報告 API 了解更多資訊。

建構子注入

物件可以透過 2 種方式接收其需要的服務。第一種選項是將服務新增為類別建構子的參數。建構子必須使用 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 ...
    }
}