简体   繁体   中英

Kotlin Spring @Value for custom data class

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?

I think that @Value is only capable of handling a "leaf" property, ie 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:

  1. @EnableConfigurationProperties(Servers::class) on a configuration class to activate support of type-safe configuration for the servers
  2. @ConfigurationProperties("servers") on Servers class, to tell Spring that Servers should be filled with data extracted from properties with servers` prefix
  3. @ConstructorBinding on Servers 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)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM