簡體   English   中英

Spring Boot - 從 application.yml 注入地圖

[英]Spring Boot - inject map from application.yml

我有一個帶有以下application.ymlSpring Boot應用application.yml - 基本上取自這里

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 是否已經支持某些內容。

下面的解決方案是@Andy Wilkinson 解決方案的簡寫,只是它不必使用單獨的類或@Bean注釋方法。

應用程序.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 解決方案中嘗試了這個想法,認為它對某人有用。

您可以使用@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

要從配置中檢索地圖,您將需要配置類。 不幸的是,@Value 注釋不起作用。

應用程序.yml

entries:
  map:
     key1: value1
     key2: value2

配置類:

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

我今天遇到了同樣的問題,但不幸的是安迪的解決方案對我不起作用。 在 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() {
       //....
    }
}

啟動應用程序后, OAuth2ProvidersService中的providers映射未初始化。 我嘗試了安迪建議的解決方案,但效果不佳。 我在那個應用程序中使用了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,請記住這一點。

使用@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 

這里,我們的地圖屬性“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...
    } }

}

更多解釋

  • \\在 yaml 中用於將字符串分成多行

  • \\"是 yaml 字符串中 "(quote) 的轉義字符

  • yaml 中的{key:value} JSON 將被 @Value 轉換為 Map

  • #{ }是SpEL 表達式,可以在@Value 中使用,轉換json int Map 或Array / list 參考

在 Spring Boot 項目中測試

在直接 @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 表達式將字符串解析為映射。

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

如果你想避免額外的結構,你可以讓它更簡單。

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

}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM