73

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.

3
  • 1
    This is fine the way it is. Spring uses JavaBean binding, so you need getters/setters. ConfigurationProperties is for typesafe configuration, it's not a data class. Commented Aug 30, 2017 at 7:53
  • 1
    See github.com/spring-projects/spring-boot/issues/8762 which is dicussing about supporting properly immutable data classes for @ConfigurationProperties. Commented Aug 30, 2017 at 13:39
  • (2021) This blog post has a complete guide for using ConfigurationProperties in Kotlin: towardsdatascience.com/… I've tested it in the latest Spring Boot (2.4.1). Basically, you need to add ConstructorBinding annotation to the data class. And add ConfigurationPropertiesScan annotation to the Application class Commented Jan 4, 2021 at 10:43

10 Answers 10

68

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") } 
Sign up to request clarification or add additional context in comments.

1 Comment

It's my understanding that kapt has replaced annotationProcessor. You would use Kotlin 1.3.50+ and plugins { ... kotlin("kapt") } and kapt(...) to wrap the dependency string instead of annotationProcessor(...)
57

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.

6 Comments

After some research this seems like the most reasonable option. It avoids having repetition with a bit dirty @Value annotation, and will also ensure that configuration is set in properties file (if related config entry is missing, exception will be thrown that the value is not initialized).
What about primitive types, since lateinit is not allowed for them?
How to access these properties from other Spring @Service or other @Component classes?
What happens if any of the late init properties here aren't present in the environment - do you get a runtime null pointer exception ?
@PaulNUK you will get an kotlin.UninitializedPropertyAccessException if you try and access the variable without initializing it.
|
26

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

2 Comments

Can you give an example on how the application.properties file should look like for the given code sample? I am having a similar scenario where i am not sure how to pass values to constructors with more than one parameter.
From spring.io/guides/tutorials/spring-boot-kotlin: example.kotlin.myService.apiToken=YourToken example.kotlin.myService.uri=YourUri
13

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

3 Comments

Sadly ConstructorBinding doesnt work well with RefreshScope and Spring Boot devs don't want to fix this. I would advice using lateinit var instead. See github.com/spring-cloud/spring-cloud-config/issues/1547
@scre_www: "Spring Boot devs don't want to fix this" - it has already been closed as "won't fix"
Thanks for this, been struggling all afternoon on the bug
7
@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 

Comments

7

As of Spring Boot 3.0 if you have a single constructor, you don't need to use @ConstructorBinding annotation anymore.

 @ConfigurationProperties("app") data class Config( val foo: String = "default foo" ) 

more info here

2 Comments

Could be default value omitted ?
Note that is true only if there's only 1 constructor: github.com/spring-projects/spring-boot/wiki/…
6
@Value("\${some.property.key:}") lateinit var foo:String 

could be used this way

2 Comments

Thanks, for you comment. I know about @Value annotation, but I do not consider this way, because it produces a lot of boilerplate code, @ConfigurationProperties definitely better.
This is just a workaround. @Value indeed should not be used as a replacement for @ConfigurationProperties, since it causes boilerplate code. The way something works ought not to be a right solution.
2

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 Comment

in version 1.3.50+, you no longer need both kapt and compile
1

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

1 Comment

This did not work for me. I also added the annotation @EnableConfigurationProperties, but still no success.
0

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 @ConstructorBinding with immutable properties don't work with @Value in Spring Boot Kotlin @ConfigurationProperties.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.