您可以开发多种不同类型的 Gradle“附加组件”,例如插件、任务、 项目扩展或工件转换,它们全部实现为可以在 JVM 上运行的类和其他类型。本章讨论这些类型的一些共同特征和概念。您可以使用这些功能来帮助实现自定义 Gradle 类型并为您的用户提供一致的 DSL。
本章适用于以下类型:
-
插件类型。
-
任务类型。
-
工件变换参数类型。
-
Worker API 工作操作参数类型。
-
使用 创建的扩展对象
ExtensionContainer.create()
,例如由插件注册的项目扩展。 -
使用创建的对象
ObjectFactory.newInstance()
。 -
为托管嵌套属性创建的对象。
-
a 的元素
NamedDomainObjectContainer
。
使用属性进行配置
您实现的自定义 Gradle 类型通常包含一些您希望可用于构建脚本和其他插件的配置。例如,下载任务可能具有指定从中下载的 URL 以及将结果写入的文件系统位置的配置。
托管物业
Gradle 提供了自己的托管属性概念,允许您将每个属性声明为抽象 getter(Java、Groovy)或抽象属性(Kotlin)。然后 Gradle 自动提供此类属性的实现。它称为托管属性,因为 Gradle 负责管理该属性的状态。属性可以是可变的,这意味着它同时具有get()
方法和set()
方法,也可以是只读的,这意味着它只有一个get()
方法。
只读属性也称为providers。
可变的托管属性
要声明可变托管属性,请添加类型的抽象 getter 方法Property<T>
- 其中T
可以是任何可序列化类型或完全 Gradle托管类型。 (有关更具体的属性类型,请参阅下面的列表。)该属性不得具有任何 setter 方法。以下是具有uri
type 属性的任务类型示例URI
:
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
且 具有public
或protected
可见性。属性类型必须是以下类型之一:
-
Property<T>
-
RegularFileProperty
-
DirectoryProperty
-
ListProperty<T>
-
SetProperty<T>
-
MapProperty<K, V>
-
ConfigurableFileCollection
-
ConfigurableFileTree
-
DomainObjectSet<T>
-
NamedDomainObjectContainer<T>
-
ExtensiblePolymorphicDomainObjectContainer<T>
-
DependencyCollector
Gradle 以与ObjectFactory相同的方式为托管属性创建值。
只读托管属性
要声明只读托管属性(也称为提供程序),请添加类型的 getter 方法Provider<T>
。然后,方法实现需要派生值,例如从其他属性派生值。
uri
以下是具有从属性派生的提供程序的任务类型的示例location
:
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 方法添加到用 注释的类型。该属性不应有任何 setter 方法。 Gradle 提供了 getter 方法的实现,并且还为属性创建了一个值。嵌套类型也被视为自定义类型,并且可以使用本章中讨论的功能。@Nested
当自定义类型具有具有相同生命周期的嵌套复杂类型时,此模式非常有用。如果生命周期不同,请考虑改用Property<NestedType>
。
以下是具有属性的任务类型的示例resource
。该Resource
类型也是自定义 Gradle 类型,并定义了一些托管属性:
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
和public
或protected
可见性。该属性不得有任何 setter 方法。此外,属性获取器必须用 进行注释。@Nested
只读托管“名称”属性
如果类型包含类型的名为“name”的抽象属性String
,Gradle 会提供 getter 方法的实现,并使用“name”参数扩展每个构造函数,该参数位于所有其他构造函数参数之前。如果类型是接口,Gradle 将提供具有单个“名称”参数和@Inject
语义的构造函数。
您可以让您的类型实现或扩展Named接口,该接口定义了这样一个只读“name”属性。
托管类型
托管类型是没有字段且其属性全部托管的抽象类或接口。也就是说,它是一种状态完全由 Gradle 管理的类型。
命名托管类型是另外具有 type 的抽象属性“name”的托管类型String
。命名托管类型作为NamedDomainObjectContainer的元素类型特别有用(见下文)。
public interface Resource {
@Input
Property<String> getHostName();
@Input
Property<String> getPath();
}
Java bean 属性。
有时您可能会看到以 Java bean 属性样式实现的属性。也就是说,它们不使用 aProperty<T>
或Provider<T>
类型,而是使用具体的 setter 和 getter 方法(或 Groovy 或 Kotlin 中的相应便利方法)实现。这种属性定义风格是 Gradle 中的遗留问题,不鼓励使用。 Gradle 核心插件中仍属于这种风格的属性将在未来版本中迁移到托管属性。
DSL 支持和可扩展性
当 Gradle 创建自定义类型的实例时,它会装饰该实例以混合 DSL 和可扩展性支持。
每个修饰实例都实现ExtensionAware,因此可以附加扩展对象。
请注意,由于向后兼容性问题,使用Project.container()创建的插件和容器元素目前尚未修饰。
服务注入
Gradle 提供了许多有用的服务,可供自定义 Gradle 类型使用。例如,任务可以使用WorkerExecutor服务来并行运行工作,如工作 API部分所示。这些服务是通过服务注入提供的。
可用服务
可提供以下注射服务:
-
ObjectFactory - 允许创建模型对象。有关更多详细信息,请参阅显式创建对象。
-
ProjectLayout - 提供对关键项目位置的访问。有关更多详细信息,请参阅惰性配置。此服务在 Worker API 操作中不可用。
-
BuildLayout - 提供对 Gradle 构建重要位置的访问。该服务仅可用于在设置插件中注入。
-
ProviderFactory - 创建
Provider
实例。有关更多详细信息,请参阅惰性配置。 -
WorkerExecutor - 允许任务并行运行。有关更多详细信息,请参阅工作 API 。
-
FileSystemOperations - 允许任务在文件系统上运行操作,例如删除文件、复制文件或同步目录。
-
ArchiveOperations - 允许任务对存档文件(例如 ZIP 或 TAR 文件)运行操作。
-
ExecOperations - 允许任务运行外部进程,并专门支持运行外部
java
程序。 -
ToolingModelBuilderRegistry - 允许插件注册 Gradle 工具 API 模型。
除上述之外,ProjectLayout
服务WorkerExecutor
仅可用于项目插件中的注入。
构造函数注入
对象可以通过两种方式接收其所需的服务。第一个选项是将服务添加为类构造函数的参数。构造函数必须带有注解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
or 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 ...
}
}
显式创建对象
更喜欢让 Gradle 使用托管属性自动创建对象。 |
自定义 Gradle 类型可以使用ObjectFactory服务创建 Gradle 类型的实例以用于其属性值。这些实例可以利用本章讨论的功能,允许您创建对象和嵌套 DSL。
在以下示例中,项目扩展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();
}
收藏类型
Gradle 提供了用于维护对象集合的类型,旨在很好地扩展 Gradle 的 DSL 并提供有用的功能,例如延迟配置。
命名域对象容器
NamedDomainObjectContainer管理一组对象,其中每个元素都有一个与其关联的名称。容器负责创建和配置元素,并提供构建脚本可用于定义和配置元素的 DSL。它旨在保存本身可配置的对象,例如一组自定义 Gradle 对象。
GradleNamedDomainObjectContainer
在整个 API 中广泛使用类型。例如,project.tasks
用于管理项目任务的对象是NamedDomainObjectContainer<Task>
.
您可以使用ObjectFactory服务创建容器实例,该服务提供ObjectFactory.domainObjectContainer()方法。这也可以使用Project.container()方法来实现,但是在自定义 Gradle 类型中,通常最好使用注入的ObjectFactory
服务而不是传递Project
实例。
您还可以使用如上所述的只读托管属性创建容器实例。
为了将类型与任何domainObjectContainer()
方法一起使用,它必须
-
是命名的托管类型;或者
-
将名为“name”的属性公开为对象的唯一且常量名称。该方法的变
domainObjectContainer(Class)
体通过调用带有字符串参数(即所需的对象名称)的类的构造函数来创建新实例。
以这种方式创建的对象被视为自定义 Gradle 类型,因此可以利用本章中讨论的功能,例如服务注入或托管属性。
请参阅上面的链接,了解domainObjectContainer()
允许自定义实例化策略的方法变体。
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 添加一个块,您可以使用它来配置容器的内容:
plugins {
id("org.gradle.sample.download")
}
download {
// Can use a block to configure the container contents
resources {
register("gradle") {
uri = uri("https://gradle.org")
}
}
}
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允许您为不同类型NamedDomainObjectContainer
的对象定义实例化策略。
命名域对象集
NamedDomainObjectSet保存一组可配置对象,其中每个元素都有一个与其关联的名称。这与 类似NamedDomainObjectContainer
,但是 aNamedDomainObjectSet
不管理集合中的对象。它们需要手动创建和添加。
命名域对象列表
NamedDomainObjectList保存可配置对象的列表,其中每个元素都有一个与其关联的名称。这与 类似NamedDomainObjectContainer
,但是 aNamedDomainObjectList
不管理集合中的对象。它们需要手动创建和添加。