了解服務與服務注入
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
僅適用於設定外掛程式和設定檔。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
是一項服務,提供對設定外掛程式或設定腳本中根目錄和設定目錄的存取,它類似於 ProjectLayout
。它是 org.gradle.api.file
套件的一部分,用於以延遲計算的值存取標準的建置範圍檔案系統位置。
這些 API 目前正在孵化中,但最終應取代設定中的現有存取器,這些存取器會傳回急切計算的位置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 專案屬性讀取其值的屬性提供者。
以下是一個簡單的範例,示範如何使用 ProviderFactory
,使用 project.providers
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
。
以下是一個使用 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
任務的建構子中。
透過一些儀式,可以在建置腳本中定義的 Ad Hoc 任務中使用 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
以產生持有注入服務的介面實作。
現在是考慮將 Ad Hoc 任務提取到適當類別中的好時機。 |
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
任務的建構子中。
透過一些儀式,可以在建置腳本中定義的 Ad Hoc 任務中使用 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
以產生持有注入服務的介面實作。
現在是考慮將 Ad Hoc 任務提取到適當類別中的好時機。 |
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
任務的建構子中。
透過一些儀式,可以在建置腳本中定義的 Ad Hoc 任務中使用 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
以產生持有注入服務的介面實作。
現在是考慮將 Ad Hoc 任務提取到適當類別中的好時機。 |
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 了解更多資訊。
建構子注入
物件可以透過 2 種方式接收其需要的服務。第一種選項是將服務新增為類別建構子的參數。建構子必須使用 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 ...
}
}