我对使用 Gradle 而不是 Maven 还比较陌生,但我认为它更适合 Kotlin 项目。
我正在使用 MySQL 数据库编写 Spring Boot 应用程序,并希望为其设置 Liquibase。该项目是使用 Spring Initializr 和 Liquibase 依赖项进行初始化的。我已经添加了(据我所知)必要的依赖项以及运行 Spring Boot 项目时运行 Liquibase 的插件。我还在网上找到的 Gradle 构建文件中配置了 Liquibase。 Liquibase 变更日志文件存在并且似乎有效;它只添加一个包含 2 列的表。
当我尝试构建(或使用 Gradle 包装器执行任何操作)时,Gradle 失败并显示
Expression 'main' cannot be invoked as a function. The function 'invoke()' is not found
在我的构建文件中包含“main”的配置行中。
以下是我的 gradle.build.kts 文件中看起来相关的摘录:
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id("org.liquibase.gradle") version "2.2.1"
kotlin("jvm") version "1.9.23"
}
java {
sourceCompatibility = JavaVersion.VERSION_21
}
repositories {
mavenCentral()
maven { url = uri("https://repo.spring.io/milestone") }
}
dependencies {
implementation("org.liquibase:liquibase-core")
runtimeOnly("com.mysql:mysql-connector-j")
}
tasks.withType<KotlinCompile> {
kotlinOptions {
freeCompilerArgs += "-Xjsr305=strict"
jvmTarget = "21"
}
}
liquibase {
activities {
main {
changeLogFile = "some/url/to/changelog/yaml/file"
url = "jdbc:mysql://localhost:3306/someDatabase"
username = "some-user"
password = "some-password"
}
}
}
这是 Gradle 包装器的输出,没有路径和行号:
$ ./gradlew build
> Configure project :
e: Expression 'main' cannot be invoked as a function. The function 'invoke()' is not found
e: Unresolved reference. None of the following candidates is applicable because of receiver type mismatch:
public val NamedDomainObjectContainer<KotlinSourceSet>.main: NamedDomainObjectProvider<KotlinSourceSet> defined in org.gradle.kotlin.dsl
public val SourceSetContainer.main: NamedDomainObjectProvider<SourceSet> defined in org.gradle.kotlin.dsl
e: Unresolved reference: changeLogFile
e: Unresolved reference: url
e: Unresolved reference: username
e: Unresolved reference: password
当我运行 ./gradlew clean 时,它也会以同样的方式失败。 当我注释掉 liquibase { ... } 配置块时 ./gradlew build 运行良好并且 ./gradlew jobs 列出了 Liquibase 特定的任务。
有人可以解释为什么失败以及我能做些什么吗?
看起来您遇到了一些“翻译问题”:您编写了适合 Groovy DSL(另一种 Gradle 语言)而不是 Kotlin DSL 的代码。
当您在
activities {}
中编写代码时,您正在运行的代码将在 NamedDomainObjectContainer<Activity>
类型的对象范围内执行。这些类型的容器在 Gradle 中随处可见(请参阅 Kotlin DSL 指南中的讨论),而 TaskContainer
(通过构建脚本中的 tasks
访问)可能是最常见的。
当您编写类似
main {}
的内容时,您正在调用一种“方法”,但该方法并未在您调用它的类中定义。在这种情况下,在 NamedDomainObjectContainer
的 Groovy Gradle 构建脚本中,这被视为名为 main
的域对象的配置,如果需要,会立即创建该对象。
Kotlin 中没有这样的魔力1。作为静态类型语言,您需要调用范围内对象上存在的方法,或者是此类对象的扩展方法(可能是作为插件的结果自动创建的)。 Kotlin DSL 文档展示了原生可用的内容。
register
2:
activities {
register("main") {
// do the configuration
}
}
这实际上是对之前 Groovy 方法的改进,因为
register
会延迟配置 main
,并且在需要 main
之前不会运行配置代码。 (事实上,在最佳 Groovy 代码中,您最终会编写与 Kotlin 相同的代码,以便从这种惰性求值中受益。)
1 Kotlin DSL 有一种不同的魔力,有时插件中定义的对象会触发代码生成,因此您可以像 Groovy 示例一样编写代码。请参阅文档。但我不相信这里的情况是这样。
2 假设main
对象还没有被创建,在这种情况下你可以调用
named