Kotlin & Spring Boot @ConfigurationProperties

SpringSpring BootKotlin

Spring Problem Overview


How to properly initialize ConfigurationProperties in Spring Boot with Kotlin?

Currently I do like in the example below:

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

But it looks pretty ugly and actually foo is not a variable, foo is constant value and should be initialized during startup and will not change in the future.

Spring Solutions


Solution 1 - Spring

With new Spring Boot 2.2 you can do like so:

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

And don't forget to include this in your dependencies in build.gradle.kts:

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

Solution 2 - Spring

Here is how I have it working with my application.yml file.

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

Here is the kotlin file:

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

This worked great for me.

Solution 3 - Spring

Update: As of Spring Boot 2.2.0, you can use data classes as follows:

@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
    )
}

For further reference, see the official documentation.


Obsolete as of Spring Boot 2.2.0, Issue closed

As stated in the docs: A "Java Beanā€œ has to be provided in order to use ConfigurationProperties. This means your properties need to have getters and setters, thus val is not possible at the moment.

> Getters and setters are usually mandatory, since binding is via standard Java Beans property descriptors, just like in Spring MVC. There are cases where a setter may be omitted [...]

This has been resolved for Spring Boot 2.2.0, which is supposed to be released soon: https://github.com/spring-projects/spring-boot/issues/8762

Solution 4 - Spring

On Spring Boot 2.4.3 with Kotlin 1.4.3 the next approach is no longer working (maybe because of a bug):

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
)

The code above starts working after implying one of the next two approaches:

  1. Add dependency
implementation("org.jetbrains.kotlin:kotlin-reflect")
  1. Update Properties class
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
)

The problem happens at the line org/springframework/boot/context/properties/ConfigurationPropertiesBindConstructorProvider.java#68

Solution 5 - Spring

@Value("\${some.property.key:}")
lateinit var foo:String

could be used this way

Solution 6 - Spring

@ConstructorBinding
@ConfigurationProperties(prefix = "your.prefix")
data class AppProperties (
    val invoiceBaseDir: String,
    val invoiceOutputFolderPdf: String,
    val staticFileFolder: String
)

Don't forget to add @ConfigurationPropertiesScan

@ConfigurationPropertiesScan
class Application

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

And finally the application.properties file:

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

Solution 7 - Spring

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"

Solution 8 - Spring

This is how I did it:

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())
    }
}

Solution 9 - Spring

In addition to what's already been said, note that val and @ConstructorBinding has some limitations. You cannot alias one variable to another. Let's say you're running in Kubernetes and want to capture the hostname, which is given by the env var HOSTNAME. The easiest way to do this is to apply @Value("\${HOSTNAME}:)" to a property, but it only works for a mutable property and without constructor binding.

See https://stackoverflow.com/q/66483609/839733.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionSoniqueView Question on Stackoverflow
Solution 1 - SpringDmitry KaltovichView Answer on Stackoverflow
Solution 2 - SpringRay HunterView Answer on Stackoverflow
Solution 3 - Springs1m0nw1View Answer on Stackoverflow
Solution 4 - SpringVlasovArtemView Answer on Stackoverflow
Solution 5 - SpringStanislavLView Answer on Stackoverflow
Solution 6 - SpringmleisterView Answer on Stackoverflow
Solution 7 - SpringEJJView Answer on Stackoverflow
Solution 8 - SpringQutoryView Answer on Stackoverflow
Solution 9 - SpringAbhijit SarkarView Answer on Stackoverflow