Spring Boot - 从application.yml注入地图

时间:2014-07-23 17:35:38

标签: java spring spring-boot

我有一个Spring Boot申请,其中包含以下application.yml - 基本上来自here

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

我可以注入特定的值,例如

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

然而,我想注入整个地图,例如:

@Value("${info}") Map<String, Object> info

那(或类似的东西)可能吗?显然,我可以直接加载yaml,但是想知道Spring是否已经支持了这些东西。

8 个答案:

答案 0 :(得分:82)

下面的解决方案是@Andy Wilkinson解决方案的简写,除了它不必使用单独的类或@Bean带注释的方法。

<强> 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<String, Integer> somedata;

    public HashMap<String, Integer> getSomedata() {
        return somedata;
    }

    public void setSomedata(HashMap<String, Integer> somedata) {
        this.somedata = somedata;
    }

}

我们可以联合@Value注释和@ConfigurationProperties,没有问题。但是getter和setter很重要,@EnableConfigurationProperties必须@ConfigurationProperties才能工作。

我从@Szymon Stepniak提供的groovy解决方案中尝试了这个想法,认为它对某人有用。

答案 1 :(得分:58)

您可以使用@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<String, Object> info = new HashMap<String, Object>();

        public Map<String, Object> getInfo() {
            return this.info;
        }
    }
}

在问题中使用yaml运行它会产生:

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

有多种选项可用于设置前缀,控制丢失属性的处理方式等。有关详细信息,请参阅javadoc

答案 2 :(得分:15)

我今天遇到了同样的问题,但不幸的是,安迪的解决方案对我不起作用。在Spring Boot 1.2.1.RELEASE中它更容易,但你必须要注意一些事情。

以下是我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地图只包含一个地图条目,我的目标是为其他OAuth提供商提供动态配置。我想将此映射注入到一个服务中,该服务将根据此yaml文件中提供的配置初始化服务。我最初的实施是:

@Service
@ConfigurationProperties(prefix = 'oauth')
class OAuth2ProvidersService implements InitializingBean {

    private Map<String, Map<String, String>> providers = [:]

    @Override
    void afterPropertiesSet() throws Exception {
       initialize()
    }

    private void initialize() {
       //....
    }
}

启动应用程序后,providers中的OAuth2ProvidersService地图未初始化。我尝试了Andy建议的解决方案,但它没有用。我在该应用程序中使用 Groovy ,因此我决定删除private并让Groovy生成getter和setter。所以我的代码看起来像这样:

@Service
@ConfigurationProperties(prefix = 'oauth')
class OAuth2ProvidersService implements InitializingBean {

    Map<String, Map<String, String>> providers = [:]

    @Override
    void afterPropertiesSet() throws Exception {
       initialize()
    }

    private void initialize() {
       //....
    }
}

在那之后小改变一切顺利。

虽然有一件事值得一提。在我开始工作之后,我决定创建这个字段private并在setter方法中为setter提供直接参数类型。不幸的是它不会那样。它会导致org.springframework.beans.NotWritablePropertyException消息:

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?

如果您在Spring Boot应用程序中使用Groovy,请记住这一点。

答案 3 :(得分:6)

使用 @Value 从编码为多行

application.yml 属性中提取地图的解决方案

application.yml

other-prop: just for demo 

my-map-property-name: "{\
         key1: \"ANY String Value here\", \  
         key2: \"any number of items\" , \ 
         key3: \"Note the Last item does not have comma\" \
         }"

other-prop2: just for demo 2 

此处,我们的地图属性“ my-map-property-name”的值以 JSON 格式存储在字符串内,并且已经使用 \ 在行尾

myJavaClass.java

import org.springframework.beans.factory.annotation.Value;

public class myJavaClass {

@Value("#{${my-map-property-name}}") 
private Map<String,String> myMap;

public void someRandomMethod (){
    if(myMap.containsKey("key1")) {
            //todo...
    } }

}

更多说明

    在Yaml中的
  • \ 用于将字符串分成多行

  • \“ 是yaml字符串中”(quote)的转义字符

  • yaml中的
  • {key:value} JSON,它将通过@Value转换为Map

  • #{} 是SpEL表达式,可以在@Value中用于转换json int Map或Array / list Reference

在spring boot项目中进行了测试

答案 4 :(得分:5)

要从配置中检索地图,您需要配置类。不幸的是,@ Value注释不会起作用。

Application.yml

entries:
  map:
     key1: value1
     key2: value2

配置类:

@Component
    @ConfigurationProperties("entries")
    @Getter
    @Setter
    public static class MyConfig {
        private Map<String, String> map;
    }

答案 5 :(得分:3)

在直接@Value 注入的情况下,最优雅的方法是将键值作为内联 json 编写(使用 ' 和 " 字符以避免繁琐的转义)并使用 SPEL 对其进行解析:

#in yaml file:
my:
  map:
      is: '{ "key1":"val1", 
              "key2":"val2" }'

在你的@Component 或 @Bean 中:

@Component
public class MyClass{
     @Value("#{${my.map.is}}")
     Map<String,String> myYamlMap;
}

为了更方便的 YAML 语法,您可以完全避免使用 json 花括号,直接键入键值对

 my:  
   map:  
       is: '"a":"b", "foo":"bar"'

并将缺少的花括号直接添加到您的@Value SPEL 表达式中:

@Value("#{{${my.map.is}}}")
 Map<String,String> myYamlMap;

该值将从 yaml 解析,包装卷曲将连接到它,最后 SPEL 表达式将字符串解析为映射。

答案 6 :(得分:2)

foo.bars.one.counter=1
foo.bars.one.active=false
foo.bars[two].id=IdOfBarWithKeyTwo

public class Foo {

  private Map<String, Bar> bars = new HashMap<>();

  public Map<String, Bar> getBars() { .... }
}

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

答案 7 :(得分:0)

如果要避免多余的结构,可以使其更简单。

service:
  mappings:
    key1: value1
    key2: value2
@Configuration
@EnableConfigurationProperties
public class ServiceConfigurationProperties {

  @Bean
  @ConfigurationProperties(prefix = "service.mappings")
  public Map<String, String> serviceMappings() {
    return new HashMap<>();
  }

}

然后像往常一样使用它,例如与构造函数一起使用:

public class Foo {

  private final Map<String, String> serviceMappings;

  public Foo(Map<String, String> serviceMappings) {
    this.serviceMappings = serviceMappings;
  }

}