Spring Boot - 从application.yml注入地图

lev*_*ied 86 java spring spring-boot

我有一个带有以下内容的Spring Boot应用程序application.yml- 基本上从这里开始:

info:
   build:
      artifact: ${project.artifactId}
      name: ${project.name}
      description: ${project.description}
      version: ${project.version}
Run Code Online (Sandbox Code Playgroud)

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

@Value("${info.build.artifact}") String value
Run Code Online (Sandbox Code Playgroud)

但是,我想注入整个地图,例如:

@Value("${info}") Map<String, Object> info
Run Code Online (Sandbox Code Playgroud)

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

rak*_*sja 93

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

application.yml:

input:
  name: raja
  age: 12
  somedata:
    abcd: 1 
    bcbd: 2
    cdbd: 3
Run Code Online (Sandbox Code Playgroud)

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;
    }

}
Run Code Online (Sandbox Code Playgroud)

我们可以同时兼顾@Value注释@ConfigurationProperties,没有任何问题.但是吸气剂和孵化器很重要,@EnableConfigurationProperties必须要有@ConfigurationProperties工作.

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

  • 谢谢!我使用spring boot 1.3.1,在我的情况下我发现不需要`@EnableConfigurationProperties` (10认同)
  • 很好的答案,但@Value注释是没有必要的. (9认同)
  • 您可以使用Lombok注释@Setter(AccessLevel.PUBLIC)和@Getter(AccessLevel.PUBLIC)来代替编写虚拟getter和setter. (2认同)

And*_*son 63

您可以使用@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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在问题中使用yaml运行此操作会产生:

{build={artifact=${project.artifactId}, version=${project.version}, name=${project.name}, description=${project.description}}}
Run Code Online (Sandbox Code Playgroud)

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


Szy*_*iak 16

我今天遇到了同样的问题,但不幸的是Andy的解决方案对我不起作用.在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
Run Code Online (Sandbox Code Playgroud)

providersmap只包含一个map条目,我的目标是为其他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() {
       //....
    }
}
Run Code Online (Sandbox Code Playgroud)

启动应用程序后,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() {
       //....
    }
}
Run Code Online (Sandbox Code Playgroud)

在那之后,一切都变得很有效.

虽然有一件事值得一提.在我开始工作之后,我决定创建这个字段,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?
Run Code Online (Sandbox Code Playgroud)

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


Con*_*ant 13

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

#in yaml file:
my:
  map:
      is: '{ "key1":"val1", 
              "key2":"val2" }'
Run Code Online (Sandbox Code Playgroud)

在您的 @Component 或 @Bean 中:

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

对于更方便的 YAML 语法,您可以完全避免 json 大括号,直接键入键值对

 my:  
   map:  
       is: '"a":"b", "foo":"bar"'
Run Code Online (Sandbox Code Playgroud)

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

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

该值将从 yaml 解析,环绕花括号将与其连接,最后 SPEL 表达式将字符串解析为映射。

  • 这正是我需要的 (2认同)

小智 10

要从配置中检索地图,您将需要配置类。不幸的是,@ Value注释无法解决问题。

Application.yml

entries:
  map:
     key1: value1
     key2: value2
Run Code Online (Sandbox Code Playgroud)

配置类:

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


msh*_*hah 9

使用@Valueapplication.yml属性中提取Map 的解决方案编码为多行

应用程序.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 
Run Code Online (Sandbox Code Playgroud)

在这里,我们的地图属性“my-map-property-name”的值以JSON格式存储在一个字符串中 ,我们在行尾使用\ 实现了多行

我的JavaClass.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...
    } }

}
Run Code Online (Sandbox Code Playgroud)

更多解释

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

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

  • yaml 中的{key:value} JSON 将被 @Value 转换为 Map

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

在 Spring Boot 项目中测试