ConfigurationProperties loading list from YML

2019-04-10 09:42发布

问题:

I'm trying to load Configuration from YML. I can load value and I can also load list if these are comma seperated values. But i can't load a typical YML List.

Configuration Class

@Component
@PropertySource("classpath:routing.yml")
@ConfigurationProperties
class RoutingProperties(){
    var angular = listOf("nothing")
    var value: String = ""
}

Working routing.yml

angular: /init, /home
value: Hello World

Not Working routing.yml

angular:
    - init
    - home

value: Hello World

Why can't i load the second version of yml / do I have a syntaxt error?

ENV: Kotlin, Spring 2.0.0.M3

回答1:

As @flyx say, @PropetySource not worked with yaml files. But in spring you may override almost everything :)

PropertySource has additional parameter: factory. It's possible to create your own PropertySourceFactory base on DefaultPropertySourceFactory

open class YamlPropertyLoaderFactory : DefaultPropertySourceFactory() {
    override fun createPropertySource(name: String?, resource: EncodedResource?): org.springframework.core.env.PropertySource<*> {
        if (resource == null)
            return super.createPropertySource(name, resource)

        return YamlPropertySourceLoader().load(resource.resource.filename, resource.resource, null)
    }
}

And when use this factory in propertysource annotation:

@PropertySource("classpath:/routing.yml", factory = YamlPropertyLoaderFactory::class)

Last that you need is to initialized variable angular with mutableList

Full code sample:

@Component
@PropertySource("classpath:/routing.yml", factory = YamlPropertyLoaderFactory::class)
@ConfigurationProperties
open class RoutingProperties {
    var angular = mutableListOf("nothing")
    var value: String = ""


    override fun toString(): String {
        return "RoutingProperties(angular=$angular, value='$value')"
    }
}

open class YamlPropertyLoaderFactory : DefaultPropertySourceFactory() {
    override fun createPropertySource(name: String?, resource: EncodedResource?): org.springframework.core.env.PropertySource<*> {
        if (resource == null)
            return super.createPropertySource(name, resource)

        return YamlPropertySourceLoader().load(resource.resource.filename, resource.resource, null)
    }
}

@SpringBootApplication
@EnableAutoConfiguration(exclude = arrayOf(DataSourceAutoConfiguration::class))
open class Application {
    companion object {
        @JvmStatic
        fun main(args: Array<String>) {
            val context = SpringApplication.run(Application::class.java, *args)

            val bean = context.getBean(RoutingProperties::class.java)

            println(bean)
        }
    }
} 


回答2:

Well, according to the docs, your YAML file will be rewritten into a property file. The first YAML file becomes:

angular=/init, /home
value=Hello World

While the second one becomes:

angular[0]=init
angular[1]=home
value=Hello World

These are obviously two very different things and therefore behave differently.

Moreover, later in the docs, it is stated that YAML does not even work with @PropertySource:

24.6.4 YAML shortcomings

YAML files can’t be loaded via the @PropertySource annotation. So in the case that you need to load values that way, you need to use a properties file.

That makes me kind of wonder why the first case works for you at all.

The docs say this about the generated …[index] properties:

To bind to properties like that using the Spring DataBinder utilities (which is what @ConfigurationProperties does) you need to have a property in the target bean of type java.util.List (or Set) and you either need to provide a setter, or initialize it with a mutable value, e.g. this will bind to the properties above

So, let's have a look at Kotlin docs: listOf returns a new read-only list of given elements. So the list is not mutable as required by the docs, which I assume is why it doesn't work. Try using a mutable list (since I have never used Kotlin, I cannot give you working code). Also try to declare it as java.util.List if that's possible in Kotlin.