Spring Boot-application.ymlからマップを挿入する


99

私は次のSpring 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ですでにサポートされているものがあるかどうか疑問に思っていました。

回答:


71

以下を使用してマップを挿入できます@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を参照してください。


アンディに感謝-これは期待どおりに機能します。興味深いのは、追加のクラスなしでは機能しないことです。つまり、何らかの理由でinfoマップを配置できませんMapBindingSample(おそらく、SpringApplication.run呼び出し中のアプリの実行に使用されているためです)。
レバント

1
サブマップを挿入する方法はありますか?たとえば、上記のマップからではinfo.buildなく注入しinfoますか?
レバントが2014

1
はい。@ConfigurationPropertiesの接頭辞をinfoに設定してから、getInfo()をgetBuild()という名前のメソッドに置き換えてTestを更新します
Andy Wilkinson 14

素敵な、アンディありがとう、魅力のように働いた!もう一つは-設定するときにlocations(別のプロパティを取得するyml代わりに、デフォルトのファイルapplication.yml上で)@ConfigurationProperties、それはプレースホルダが交換されているには至らなかった場合を除き、働いていました。たとえば、システムプロパティをproject.version=123設定している場合、回答で指定した例はを返しversion=123、設定後locationsはを返しproject.version=${project.version}ます。ここにある種の制限があるかどうか知っていますか?
レバントは14

これは制限です。カスタムの場所を使用するときにプレースホルダーの置換を実行するために問題(github.com/spring-projects/spring-boot/issues/1301)を開きました
アンディ・ウィルキンソン

108

以下の解決策は、@ 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ます。問題はありません。しかし、ゲッターとセッターは重要であり@EnableConfigurationProperties@ConfigurationProperties動作させるために必要です。

@Szymon Stepniakが提供するグルーヴィーなソリューションからこのアイデアを試しましたが、誰かに役立つと思いました。


11
ありがとう!私はスプリングブート1.3.1を使用しましたが、私の場合は必要ないことがわかりました@EnableConfigurationProperties
zhuguowei 2015

この回答を使用すると、「無効な文字定数」エラーが発生します。このエラーを防ぐために、@ ConfigurationProperties(prefix = 'input')を変更して二重引用符を使用できますか?
アントンランド2017

10
良い答えですが、@ Valueアノテーションは必要ありません。
Robin

3
ダミーのゲッターとセッターを作成する代わりに、Lombokアノテーション@Setter(AccessLevel.PUBLIC)と@Getter(AccessLevel.PUBLIC)を使用できます
RiZKiT

天才。地図<文字列、地図<文字列、文字列>>:設定もネストすることができることに注意してください
Máthéエンドレ-Botond

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

providersマップにはマップエントリが1つだけ含まれています。私の目標は、他の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が初期化されませんでした。アンディが提案した解決策を試しましたが、うまくいきませんでした。私はそのアプリケーションでGroovyを使用しているので、削除privateしてGroovyにゲッターとセッターを生成させることにしました。だから私のコードは次のようになりました:

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

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

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

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

その小さな変更の後、すべてが機能しました。

言及する価値があるかもしれないことが1つありますが。それを機能させた後、私はこのフィールドを作成privateし、セッターメソッドにストレート引数タイプをセッターに提供することを決定しました。残念ながらそれはうまくいきません。これは、原因となる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を使用している場合は注意してください。


14

構成からマップを取得するには、構成クラスが必要です。残念ながら、@ Valueアノテーションはうまく機能しません。

Application.yml

entries:
  map:
     key1: value1
     key2: value2

構成クラス:

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

上記のソリューションがバージョン2.1.0に対して機能することをテスト
Tugrul ASLAN

6

マルチラインとしてコード化されたapplication.ymlプロパティ から@Valueを 使用してマッププルするためのソリューション

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文字列の"(引用) "のエスケープ文字です

  • {key:value} @ValueによってMapに変換されるyamlのJSON

  • #{}これはSpEL式であり、@ Valueでjson intマップまたは配列/リストの参照を変換するために使用できます

Spring Bootプロジェクトでテスト済み


3
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
Stack Overflowへようこそ!このコードスニペットは問題を解決する可能性がありますが、説明を含めると、投稿の品質を向上させるのに役立ちます。あなたは将来の読者のための質問に答えていることを覚えておいてください、そしてそれらの人々はあなたのコード提案の理由を知らないかもしれません。
Scott Weldon 2016

ただし、wikiへのリンクは重要です。説明はgithub.com/spring-projects/spring-boot/wiki/…にあります
dschulten

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

}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.