我最近为 Spring Boot 项目将 Gradle 从 4.x 升级到了 6.6。我以为我终于把它全部启动并运行了,但后来意识到我们的一个应用程序可能启动两个类型为
BootRun
(例如 A 和 B)的不同配置任务,但无法启动第二个 B 实例。
这是我尝试运行第二个实例时的错误:
Build file 'C:\Users\...\build.gradle' line: 17
Execution failed for task ':apps:myapp:bootRunB'.
> The value for task ':apps:myapp:bootRunB' property 'mainClass' is final and cannot be changed any further.
这是我的
build.gradle
文件中配置任务的部分:
task bootRunB(type: org.springframework.boot.gradle.tasks.run.BootRun, dependsOn: 'build') {
group = 'Application'
doFirst() {
main = bootJar.mainClassName
classpath = bootRun.classpath
systemProperty '...'
}
}
如有任何建议,我们将不胜感激。
Gradle 最近引入了一个用于“惰性配置”的 API,现在插件扩展和任务类型等内部功能已迁移到这个新 API。总之,可以说,构建脚本中的(几乎)每个配置属性都应该使用 Property<T>
或
Provider<T>
(只读)来实现,而不仅仅是简单类型的 getter 和 setter
T
。为了提供向后兼容性,许多属性没有更改,但添加了新属性,并且旧属性读取和写入这些新属性。您的问题也是如此,因为 Spring Boot Gradle 插件中的任务类型 扩展了 Gradle 提供的
JavaExec
任务类型。添加了新属性
mainClass
(a Property<string>
),并修改了旧属性 main
以使用新属性。方法 getMain()
使用 mainClass
从 Property.get()
读取,方法 setMain(String)
(在 Groovy 中使用语法 main = '...'
时调用)使用 mainClass
写入 Property.set(String)
。这一切都可以正常工作,但是 Gradle 为其惰性配置 API 引入了一些附加功能。这些功能之一是可写属性的最终确定(Property<>
)。在构建过程中的某个时刻,将出于其原始目的(运行任务)读取任务属性(例如
mainClass
),因此该点之后的任何更改都不会生效。在 Gradle 的早期版本中,这会导致很多难以调试的问题,因为 Gradle 不会显示任何错误。现在,一旦出于最初目的而读取这些属性,它们就会被最终确定,从而导致当有人随后尝试更改它们时 Gradle 会失败。关于您的用例,属性mainClass
(由
main
访问)已在doFirst
闭包中最终确定,因此您需要提前应用此配置。由于您想要将该值设置为属性 bootJar.mainClassName
的值(这是一个简单的 String
),因此您必须确保该属性在读取用于配置 bootRunB
任务之前具有其最终值:bootJar {
mainClassName = '...'
}
task bootRunB(type: org.springframework.boot.gradle.tasks.run.BootRun, dependsOn: 'build') {
group = 'Application'
main = bootJar.mainClassName
// From this point, bootRun.classpath must not be changed !
classpath = bootRun.classpath
systemProperty '...'
}
要消除对配置顺序的依赖,您可以使用
Provider<String>
Project.provider(...)
task bootRunB(type: org.springframework.boot.gradle.tasks.run.BootRun, dependsOn: 'build') {
group = 'Application'
main = provider({ bootJar.mainClassName })
classpath = bootRun.classpath
systemProperty '...'
}
task bootRunB(type: org.springframework.boot.gradle.tasks.run.BootRun, dependsOn: 'build') {
group = 'Application'
mainClass = 'com.App'
doFirst() {
main = bootJar.mainClassName
classpath = bootRun.classpath
systemProperty '...'
}
}
它现在提供了一个需要提供者的新属性。 不要填写 main 属性,而是填写 mainClass 属性:
kts脚本语言示例:
tasks.register<BootRun>("myBootRunTask") {
dependsOn("assemble")
group = "Application"
val bootJarTask = tasks.getByName<BootJar>("bootJar")
mainClass.set(provider { bootJarTask.mainClassName })
classpath = bootJarTask.classpath
...
}