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 仅在 Settings 插件和 Settings 文件中可用。ProjectLayout 在 Worker API 操作中不可用。

虽然从技术上讲可以注入其他服务,但这种做法不受支持,将来可能会导致破坏性更改。仅注入上面明确列出的服务,以确保稳定性和兼容性。

您应避免注入未在此处列出的类型。

1. ObjectFactory

ObjectFactory 是一项用于创建自定义 Gradle 类型的服务,它允许您在构建逻辑中定义嵌套对象和 DSL。它提供了创建不同类型实例的方法,例如属性 (Property<T>)、集合 (ListProperty<T>, SetProperty<T>, MapProperty<K, V>)、文件相关对象 (RegularFileProperty, DirectoryProperty, ConfigurableFileCollection, ConfigurableFileTree) 等等。

您可以使用 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 是一项服务,它在 Settings 插件或 Settings 脚本中提供对根目录和 settings 目录的访问,类似于 ProjectLayout。它是 org.gradle.api.file 包的一部分,用于以延迟计算值的形式访问标准的构建范围文件系统位置。

这些 API 目前处于孵化阶段,但最终应替换 Settings 中返回急切计算位置的现有访问器
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 接口。该插件期望使用 @Inject 注解将 BuildLayout 实例注入到其构造函数中。

4. ProviderFactory

ProviderFactory 是一项服务,它提供了创建不同类型 providers 的方法。Providers 用于建模构建脚本中可能延迟计算的值。

ProviderFactory 接口提供了创建各种类型 providers 的方法,包括

  • provider(Callable<T> value) 用于创建一个 provider,其值基于 Callable 延迟计算。

  • provider(Provider<T> value) 用于创建一个简单包装现有 provider 的 provider。

  • property(Class<T> type) 用于为特定类型创建属性 provider。

  • gradleProperty(Class<T> type) 用于创建一个属性 provider,它从 Gradle 项目属性中读取其值。

这里有一个简单示例,演示使用 project.providers 使用 ProviderFactory

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 任务的构造函数中。

通过一些繁琐的步骤,可以在构建脚本中定义的临时任务中使用 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 来生成包含注入服务的接口实现。

这是一个考虑将临时任务提取到适当类中的好时机。

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 任务的构造函数中。

通过一些繁琐的步骤,可以在构建脚本中定义的临时任务中使用 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 来生成包含注入服务的接口实现。

这是一个考虑将临时任务提取到适当类中的好时机。

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 任务的构造函数中。

通过一些繁琐的步骤,可以在构建脚本中定义的临时任务中使用 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 来生成包含注入服务的接口实现。

这是一个考虑将临时任务提取到适当类中的好时机。

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 ...
    }
}