Issue
I want to be able to do something like the following:
#application.yml
servers:
foo:
name: "Foo"
url: "http://localhost:8080/foo"
description: "Foo foo"
bar:
name: "Bar"
url: "http://localhost:8080/bar"
description: "Bar bar"
data class Server (
val name : String,
val url : String,
val description : String
)
Then somewhere in the code
@Service
class LookupService (
@Value ("\${servers.foo}")
val fooServer : Server,
@Value ("\${servers.bar}")
val barServer : Server
) {
// do stuff
}
When I try it currently I get a java.lang.IllegalArgumentException: Could not resolve placeholder 'servers.bar' in value "${servers.bar}"
on starting up the app.
Is there a simple way to do this without specifically doing an @Value
on each property?
Solution
I think that @Value
is only capable of handling a "leaf" property, i.e. a property with a single value, not a property with children.
This is my understanding of type-safe configuration properties documentation.
What you can do in your case is prepare a Servers
structure that will contain map your entire configuration tree up to a certain point. In your case, you can create it with foo
and bar
attributes of type Server
.
For it to fully work, you need to put 3 annotations in your code:
@EnableConfigurationProperties(Servers::class)
on a configuration class to activate support of type-safe configuration for the servers@ConfigurationProperties("servers") on
Serversclass, to tell Spring that Servers should be filled with data extracted from properties with
servers` prefix@ConstructorBinding
onServers
class, to tell Spring it is immutable, and values must be injected using constructor.
You will find a working minimal example below:
@SpringBootApplication
@EnableConfigurationProperties(Servers::class)
class DemoApplication
fun main(args: Array<String>) {
runApplication<DemoApplication>(*args)
}
data class Server(val name: String, val url: URL, val description: String)
@ConfigurationProperties("servers")
@ConstructorBinding
data class Servers(val foo: Server, val bar: Server)
@Service
class LookupService(servers : Servers) {
val foo = servers.foo
val bar = servers.bar
init {
println(foo)
println(bar)
}
}
When started, the example app prints injected configuration:
Server(name=Foo, url=http://localhost:8080/foo, description=Foo foo)
Server(name=Bar, url=http://localhost:8080/bar, description=Bar bar)
Answered By - amanin
Answer Checked By - Marilyn (JavaFixing Volunteer)