【问题标题】:Kotlin & Spring Boot @ConfigurationPropertiesKotlin 和 Spring Boot @ConfigurationProperties
【发布时间】:2018-02-07 17:41:50
【问题描述】:

如何使用 KotlinSpring Boot 中正确初始化 ConfigurationProperties?

目前我喜欢下面的例子:

 @ConfigurationProperties("app")
 class Config {
     var foo: String? = null
 }

但它看起来很丑,实际上foo 不是variable,foo 是常量 value 并且应该在启动期间初始化并且不会在未来

【问题讨论】:

  • 这很好。 Spring 使用 JavaBean 绑定,因此您需要 getter/setter。 ConfigurationProperties 用于类型安全配置,它不是 data 类。
  • 参见github.com/spring-projects/spring-boot/issues/8762,它正在讨论为@ConfigurationProperties 支持正确的不可变数据类。
  • (2021) 这篇博文提供了在 Kotlin 中使用 ConfigurationProperties 的完整指南:towardsdatascience.com/… 我已经在最新的 Spring Boot (2.4.1) 中对其进行了测试。基本上,您需要在数据类中添加 ConstructorBinding 注解。并在Application类中添加ConfigurationPropertiesScan注解

标签: spring spring-boot kotlin


【解决方案1】:

这是我使用 application.yml 文件的方式。

myconfig:
  my-host: ssl://example.com
  my-port: 23894
  my-user: user
  my-pass: pass

这是 kotlin 文件:

@Configuration
@ConfigurationProperties(prefix = "myconfig")
class MqttProperties {
    lateinit var myHost: String
    lateinit var myPort: String
    lateinit var myUser: String
    lateinit var myPass: String    
}

这对我很有用。

【讨论】:

  • 经过一些研究,这似乎是最合理的选择。它避免了带有一点脏的@Value注解的重复,并且还将确保在属性文件中设置配置(如果缺少相关配置条目,将抛出该值未初始化的异常)。
  • 原始类型怎么办,因为它们不允许使用lateinit
  • 如何从其他 Spring @Service 或其他 @Component 类访问这些属性?
  • @kiltek 您可以获取创建的配置 bean 并在其他组件和服务中使用该 bean。
  • 如果环境中不存在这里的任何后期初始化属性会发生什么 - 你会得到运行时空指针异常吗?
【解决方案2】:

使用新的 Spring Boot 2.2,您可以这样做:

@ConstructorBinding
@ConfigurationProperties(prefix = "swagger")
data class SwaggerProp(
    val title: String, val description: String, val version: String
)

并且不要忘记将其包含在 build.gradle.kts 的依赖项中:

dependencies {
  annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
}

【讨论】:

  • 我的理解是kapt已经取代了annotationProcessor。您将使用 Kotlin 1.3.50+ 和 plugins { ... kotlin("kapt") }kapt(...) 来包装依赖字符串而不是 annotationProcessor(...)
【解决方案3】:

更新:从 Spring Boot 2.2.0 开始,您可以使用如下数据类:

@ConstructorBinding
@ConfigurationProperties("example.kotlin")
data class KotlinExampleProperties(
        val name: String,
        val description: String,
        val myService: MyService) {

    data class MyService(
            val apiToken: String,
            val uri: URI
    )
}

如需进一步参考,请参阅official documentation


自 Spring Boot 2.2.0 起已过时,问题已关闭

正如docs 中所述:必须提供“Java Bean”才能使用ConfigurationProperties。这意味着您的属性需要有getter 和setter,因此val暂时不可以。

Getter 和 setter 通常是强制性的,因为绑定是通过标准的 Java Bean 属性描述符进行的,就像在 Spring MVC 中一样。在某些情况下,可以省略 setter [...]

这个问题已经在 Spring Boot 2.2.0 中得到解决,应该很快就会发布: https://github.com/spring-projects/spring-boot/issues/8762

【讨论】:

  • 您能否举例说明给定代码示例的 application.properties 文件应该是什么样子?我遇到了类似的情况,我不确定如何将值传递给具有多个参数的构造函数。
  • 来自spring.io/guides/tutorials/spring-boot-kotlin: example.kotlin.myService.apiToken=YourToken example.kotlin.myService.uri=YourUri
【解决方案4】:

在带有 Kotlin 1.4.3 的 Spring Boot 2.4.3 上,下一个方法不再有效(可能是由于错误):

import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.context.properties.EnableConfigurationProperties

@SpringBootApplication
@EnableConfigurationProperties(TestProperties::class)
class Application

fun main(args: Array<String>) {
    runApplication<Application>(*args)
}
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.context.properties.ConstructorBinding

@ConfigurationProperties(prefix = "test")
@ConstructorBinding
data class TestProperties(
    val value: String
)

上面的代码在暗示以下两种方法之一后开始工作:

  1. 添加依赖项
implementation("org.jetbrains.kotlin:kotlin-reflect")
  1. 更新属性类
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.context.properties.ConstructorBinding

@ConfigurationProperties(prefix = "test")
data class TestProperties @ConstructorBinding constructor(
    val value: String
)

问题发生在 org/springframework/boot/context/properties/ConfigurationPropertiesBindConstructorProvider.java#68 行

【讨论】:

【解决方案5】:
@Value("\${some.property.key:}")
lateinit var foo:String

可以这样使用

【讨论】:

  • 感谢您的评论。我知道@Value注解,但我不考虑这种方式,因为它会产生很多样板代码,@ConfigurationProperties肯定更好。
  • 这只是一种解决方法。 @Value 确实不应该用作 @ConfigurationProperties 的替代品,因为它会导致样板代码。事情的运作方式不应该是正确的解决方案。
【解决方案6】:

application.properties

metro.metro2.url= ######

Metro2Config.kt

@Component
@ConfigurationProperties(prefix = "metro")
data class Metro2PropertyConfiguration(

        val metro2: Metro2 = Metro2()
)

data class Metro2(
    var url: String ?= null
)

build.gradle

Plugins:
id 'org.jetbrains.kotlin.kapt' version '1.2.31'

// kapt dependencies required for IntelliJ auto complete of kotlin config properties class
    kapt "org.springframework.boot:spring-boot-configuration-processor"
    compile "org.springframework.boot:spring-boot-configuration-processor"

【讨论】:

  • 在 1.3.50+ 版本中,您不再需要 kapt 和 compile
【解决方案7】:
@ConstructorBinding
@ConfigurationProperties(prefix = "your.prefix")
data class AppProperties (
    val invoiceBaseDir: String,
    val invoiceOutputFolderPdf: String,
    val staticFileFolder: String
)

别忘了加@ConfigurationPropertiesScan

@ConfigurationPropertiesScan
class Application

fun main(args: Array<String>) {
    runApplication<Application>(*args)
}

最后是 application.properties 文件:

your.prefix.invoiceBaseDir=D:/brot-files
your.prefix.invoiceOutputFolderPdf=invoices-pdf
your.prefix.staticFileFolder=static-resources

【讨论】:

    【解决方案8】:

    我就是这样做的:

    application.properties

    my.prefix.myValue=1
    

    MyProperties.kt

    import org.springframework.boot.context.properties.ConfigurationProperties
    import org.springframework.stereotype.Component
    
    @Component
    @ConfigurationProperties(prefix = "my.prefix")
    class MyProperties
    {
        private var myValue = 0
        fun getMyValue(): Int {
            return myValue;
        }
    
        fun setMyValue(value: Int){
            myValue = value
        }
    }
    

    MyService.kt

    @Component
    class MyService(val myProperties: MyProperties) {
        fun doIt() {
            System.console().printf(myProperties.getMyValue().toString())
        }
    }
    

    【讨论】:

    • 这对我不起作用。我还加了注解@EnableConfigurationProperties,还是没有成功。
    【解决方案9】:

    除了已经说过的,请注意val@ConstructorBinding 有一些限制。您不能将一个变量别名为另一个变量。假设您在 Kubernetes 中运行并想要捕获主机名,该主机名由环境变量 HOSTNAME 给出。最简单的方法是将@Value("\${HOSTNAME}:)" 应用于属性,但它仅适用于可变属性并且没有构造函数绑定。

    @ConstructorBinding with immutable properties don't work with @Value in Spring Boot Kotlin @ConfigurationProperties

    【讨论】:

      猜你喜欢
      • 2018-06-27
      • 1970-01-01
      • 2020-01-07
      • 2015-06-17
      • 2016-07-24
      • 2018-08-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多