I have a SpringBoot 2, Kotlin/Java web service that uses @ConfigurationProperties to load up a nested set of classes/properties for its use at runtime. Everything is working properly except for one thing: I cannot figure out how to pass a value (code) from the parent class (TenantProperties) to its nested child class (DatabaseProperties). In the following snippet, which is extracted from another parent with the proper annotations, I am trying to get the code in TenantProperties to be passed to the DatabaseProperties when it is constructed. No matter what I've tried, the parent always passes null to the child. I'd appreciate any suggestions.
class TenantProperties(
var code: String? = null,
var id: String? = null,
var name: String? = null,
var db: DatabaseProperties = DatabaseProperties(code),
var blob: BlobProperties = BlobProperties(code)
) {
class DatabaseProperties(
var code: String?,
var uriTemplate: String? = null,
var username: String? = null,
var password: String? = null
) {
fun connectionString() = "$uriTemplate".format(username, password, code)
}
}
In your example you initialise the classes along with the parent object. Spring configuration parameters are fed in at a later point. So your nested classes are always initialised with the default value, so a configured code
will never be passed down.
In order to get your code to work, I suggest to make the classes inner classes (in order to allow them to access the outer one) and have the post-built property initialised lazily:
@Configuration
@ConfigurationProperties(value = "my")
class TenantProperties {
lateinit var code: String
lateinit var id: String
lateinit var name: String
val db = DatabaseProperties()
inner class DatabaseProperties {
lateinit var uriTemplate: String
lateinit var username: String
lateinit var password: String
val connectionString by lazy { "$uriTemplate".format(username, password, code) }
}
}
Making all variables lateinit
you gain some type safety. In order to make it even more robust, you can add some validators:
@Configuration
@ConfigurationProperties(value = "my", ignoreUnknownFields = false, ignoreInvalidFields = false)
class TenantProperties {
@NotNull lateinit var code: String
@NotNull lateinit var id: String
@NotNull lateinit var name: String
val db = DatabaseProperties()
inner class DatabaseProperties {
@NotNull lateinit var uriTemplate: String
@NotNull lateinit var username: String
@NotNull lateinit var password: String
val connectionString by lazy { "$uriTemplate".format(username, password, code) }
}
}
now, your application.properties
can look like this:
my.code=aCode
my.id=anId
my.name=aName
my.db.uri-template=http://localhost
my.db.username=admin
my.db.password=pw