理解服务和服务注入
Gradle 提供了许多有用的服务,可供自定义 Gradle 类型使用。例如,WorkerExecutor 服务可以被任务使用以并行运行工作,如 worker API 部分所示。这些服务通过服务注入提供。
可用服务
以下服务可用于注入
-
ObjectFactory
- 允许创建模型对象。 -
ProjectLayout
- 提供对关键项目位置的访问。 -
BuildLayout
- 提供对 Gradle 构建的重要位置的访问。 -
ProviderFactory
- 创建Provider
实例。 -
WorkerExecutor
- 允许任务并行运行工作。 -
FileSystemOperations
- 允许任务在文件系统上运行操作,例如删除文件、复制文件或同步目录。 -
ArchiveOperations
- 允许任务在存档文件上运行操作,例如 ZIP 或 TAR 文件。 -
ExecOperations
- 允许任务运行外部进程,并专门支持运行外部java
程序。 -
ToolingModelBuilderRegistry
- 允许插件注册 Gradle Tooling API 模型。 -
TestEventReporterFactory
- 允许插件访问 Gradle 的测试事件及其对应的 API。
在以上服务中,ProjectLayout
和 WorkerExecutor
服务仅在项目插件中可用于注入。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
创建属性并设置其值
tasks.register("myObjectFactoryTask") {
doLast {
val objectFactory = project.objects
val myProperty = objectFactory.property(String::class)
myProperty.set("Hello, Gradle!")
println(myProperty.get())
}
}
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
属性使该对象可用
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
的示例
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) {}
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
实例。这是一个简单的示例
tasks.register("showLayout") {
doLast {
val layout = project.layout
println("Project Directory: ${layout.projectDirectory}")
println("Build Directory: ${layout.buildDirectory.get()}")
}
}
tasks.register('showLayout') {
doLast {
def layout = project.layout
println "Project Directory: ${layout.projectDirectory}"
println "Build Directory: ${layout.buildDirectory.get()}"
}
}
这是一个使用 javax.inject.Inject
的示例
abstract class MyProjectLayoutTask
@Inject constructor(private var projectLayout: ProjectLayout) : DefaultTask() {
@TaskAction
fun doTaskAction() {
val outputDirectory = projectLayout.projectDirectory
println(outputDirectory)
}
}
tasks.register("myInjectedProjectLayoutTask", MyProjectLayoutTask::class) {}
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.rootDir → Settings.layout.rootDirectory Settings.settingsDir → Settings.layout.settingsDirectory |
你可以使用 settings.layout
属性从 Settings
对象获取 BuildLayout
实例。这是一个简单的示例
println("Root Directory: ${settings.layout.rootDirectory}")
println("Settings Directory: ${settings.layout.settingsDirectory}")
println "Root Directory: ${settings.layout.rootDirectory}"
println "Settings Directory: ${settings.layout.settingsDirectory}"
这是一个使用 javax.inject.Inject
的示例
abstract class MyBuildLayoutPlugin @Inject constructor(private val buildLayout: BuildLayout) : Plugin<Settings> {
override fun apply(settings: Settings) {
println(buildLayout.rootDirectory)
}
}
apply<MyBuildLayoutPlugin>()
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 项目属性读取其值的属性提供器。
这是一个简单的示例,演示了使用 project.providers
使用 ProviderFactory
的用法
tasks.register("printMessage") {
doLast {
val providerFactory = project.providers
val messageProvider = providerFactory.provider { "Hello, Gradle!" }
println(messageProvider.get())
}
}
tasks.register('printMessage') {
doLast {
def providerFactory = project.providers
def messageProvider = providerFactory.provider { "Hello, Gradle!" }
println messageProvider.get()
}
}
名为 printMessage
的任务使用 ProviderFactory
创建一个 provider
,该 provider
提供消息字符串。
这是一个使用 javax.inject.Inject
的示例
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) {}
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
的基本示例
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) {}
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
的示例
abstract class MyFileSystemOperationsTask
@Inject constructor(private var fileSystemOperations: FileSystemOperations) : DefaultTask() {
@TaskAction
fun doTaskAction() {
fileSystemOperations.sync {
from("src")
into("dest")
}
}
}
tasks.register("myInjectedFileSystemOperationsTask", MyFileSystemOperationsTask::class)
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
interface InjectedFsOps {
@get:Inject val fs: FileSystemOperations
}
tasks.register("myAdHocFileSystemOperationsTask") {
val injected = project.objects.newInstance<InjectedFsOps>()
doLast {
injected.fs.copy {
from("src")
into("dest")
}
}
}
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
的示例
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)
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
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)
}
}
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
的示例
abstract class MyExecOperationsTask
@Inject constructor(private var execOperations: ExecOperations) : DefaultTask() {
@TaskAction
fun doTaskAction() {
execOperations.exec {
commandLine("ls", "-la")
}
}
}
tasks.register("myInjectedExecOperationsTask", MyExecOperationsTask::class)
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
interface InjectedExecOps {
@get:Inject val execOps: ExecOperations
}
tasks.register("myAdHocExecOperationsTask") {
val injected = project.objects.newInstance<InjectedExecOps>()
doLast {
injected.execOps.exec {
commandLine("ls", "-la")
}
}
}
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 插件中使用。
这是一个简化的示例
// 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())
}
}
// 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 了解更多信息。
构造器注入
对象可以通过两种方式接收它需要的服务。第一种选择是将服务添加为类构造函数的参数。构造函数必须使用 javax.inject.Inject
注解进行注释。Gradle 使用每个构造函数参数的声明类型来确定对象需要的服务。构造函数参数的顺序及其名称并不重要,你可以随意设置。
这是一个示例,展示了通过其构造函数接收 ObjectFactory
的任务类型
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 方法必须是 public
或 protected
。该方法可以是 abstract
,或者,在不可能的情况下,可以有一个虚拟方法体。方法体将被丢弃。
这是一个示例,展示了通过属性 getter 方法接收两个服务的任务类型
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 ...
}
}