Spring Boot - inject map from application.yml

匿名 (未验证) 提交于 2019-12-03 02:49:01

问题:

I have a Spring Boot application with the following application.yml - taken basically from here:

info:    build:       artifact: ${project.artifactId}       name: ${project.name}       description: ${project.description}       version: ${project.version} 

I can inject particular values, e.g.

@Value("${info.build.artifact}") String value 

I would like, however, to inject the whole map, i.e. something like this:

@Value("${info}") Map info 

Is that (or something similar) possible? Obviously, I can load yaml directly, but was wondering if there's something already supported by Spring.

回答1:

You can have a map injected using @ConfigurationProperties:

import java.util.HashMap; import java.util.Map;  import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;  @Configuration @EnableAutoConfiguration @EnableConfigurationProperties public class MapBindingSample {      public static void main(String[] args) throws Exception {         System.out.println(SpringApplication.run(MapBindingSample.class, args)                 .getBean(Test.class).getInfo());     }      @Bean     @ConfigurationProperties     public Test test() {         return new Test();     }      public static class Test {          private Map info = new HashMap();          public Map getInfo() {             return this.info;         }     } } 

Running this with the yaml in the question produces:

{build={artifact=${project.artifactId}, version=${project.version}, name=${project.name}, description=${project.description}}} 

There are various options for setting a prefix, controlling how missing properties are handled, etc. See the javadoc for more information.



回答2:

Below solution is a shorthand for @Andy Wilkinson's solution, except that it doesn't have to use a separate class or on a @Bean annotated method.

application.yml:

input:   name: raja   age: 12   somedata:     abcd: 1      bcbd: 2     cdbd: 3 

SomeComponent.java:

@Component @EnableConfigurationProperties @ConfigurationProperties(prefix = "input") class SomeComponent {      @Value("${input.name}")     private String name;      @Value("${input.age}")     private Integer age;      private HashMap somedata;      public HashMap getSomedata() {         return somedata;     }      public void setSomedata(HashMap somedata) {         this.somedata = somedata;     }  } 

We can club both @Value annotation and @ConfigurationProperties, no issues. But getters and setters are important and @EnableConfigurationProperties is must to have the @ConfigurationProperties to work.

I tried this idea from groovy solution provided by @Szymon Stepniak, thought it will be useful for someone.



回答3:

I run into the same problem today, but unfortunately Andy's solution didn't work for me. In Spring Boot 1.2.1.RELEASE it's even easier, but you have to be aware of a few things.

Here is the interesting part from my application.yml:

oauth:   providers:     google:      api: org.scribe.builder.api.Google2Api      key: api_key      secret: api_secret      callback: http://callback.your.host/oauth/google 

providers map contains only one map entry, my goal is to provide dynamic configuration for other OAuth providers. I want to inject this map into a service that will initialize services based on the configuration provided in this yaml file. My initial implementation was:

@Service @ConfigurationProperties(prefix = 'oauth') class OAuth2ProvidersService implements InitializingBean {      private Map> providers = [:]      @Override     void afterPropertiesSet() throws Exception {        initialize()     }      private void initialize() {        //....     } } 

After starting the application, providers map in OAuth2ProvidersService was not initialized. I tried the solution suggested by Andy, but it didn't work as well. I use Groovy in that application, so I decided to remove private and let Groovy generates getter and setter. So my code looked like this:

@Service @ConfigurationProperties(prefix = 'oauth') class OAuth2ProvidersService implements InitializingBean {      Map> providers = [:]      @Override     void afterPropertiesSet() throws Exception {        initialize()     }      private void initialize() {        //....     } } 

After that small change everything worked.

Although there is one thing that might be worth mentioning. After I make it working I decided to make this field private and provide setter with straight argument type in the setter method. Unfortunately it wont work that. It causes org.springframework.beans.NotWritablePropertyException with message:

Invalid property 'providers[google]' of bean class [com.zinvoice.user.service.OAuth2ProvidersService]: Cannot access indexed value in property referenced in indexed property path 'providers[google]'; nested exception is org.springframework.beans.NotReadablePropertyException: Invalid property 'providers[google]' of bean class [com.zinvoice.user.service.OAuth2ProvidersService]: Bean property 'providers[google]' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter? 

Keep it in mind if you're using Groovy in your Spring Boot application.



回答4:

foo.bars.one.counter=1 foo.bars.one.active=false foo.bars[two].id=IdOfBarWithKeyTwo  public class Foo {    private Map bars = new HashMap();    public Map getBars() { .... } } 

https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-Configuration-Binding



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!