Spring Boot-application.yml에서 맵 삽입


99

나는이 봄 부트 다음에 응용 프로그램 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 을 참조하십시오.


감사합니다 Andy-예상대로 작동합니다. 추가 클래스 없이는 작동하지 않는다는 것이 흥미 롭습니다. 즉 , 어떤 이유로 info지도를 넣을 수 없습니다 MapBindingSample(아마도 SpringApplication.run호출 에서 앱을 실행하는 데 사용되기 때문일 수 있습니다 ).
levant pied jul

1
서브맵을 삽입하는 방법이 있습니까? 예 를 들어 위의 맵 info.build대신 주입 info합니까?
levant pied

1
예. 테스트 () getBuild라는 방법으로 된 getInfo ()를 대체하는 업데이트 한 후 정보에 @ConfigurationProperties에 접두사를 설정하고
앤디 윌킨슨

감사합니다 Andy, 매력처럼 작동했습니다! 한 가지 더-을 설정 하면 (기본값 대신 locations다른 yml파일 에서 속성을 가져 오기 위해 application.yml) @ConfigurationProperties자리 표시자가 바뀌지 않는 것을 제외하고는 작동했습니다. 예를 project.version=123들어 시스템 속성을 설정 한 경우 답변에 제공 한 예제는를 반환 version=123하고 설정 locations후에는을 반환 project.version=${project.version}합니다. 여기에 어떤 종류의 제한이 있는지 알고 있습니까?
levant pied

그것은 한계입니다. 나는 문제 (연 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

이 답변을 사용할 때 '잘못된 문자 상수'오류가 발생합니다. 이 오류를 방지하기 위해 큰 따옴표를 사용하도록 @ConfigurationProperties (prefix = 'input')을 변경할 수 있습니다.
Anton Rand

10
좋은 대답이지만 @Value 주석은 필요하지 않습니다.
로빈

3
대신 더미 게터 및 당신은 롬복 주석 @Setter (AccessLevel.PUBLIC)와 @Getter (AccessLevel.PUBLIC)를 사용할 수 있습니다 세터 작성
RiZKiT

관대 한. 구성도 중첩 될 수 있습니다. Map <String, Map <String, String >>
Máthé Endre-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지도에는지도 항목이 하나만 포함되어 있습니다. 내 목표는 다른 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 를 사용 하기 때문에 privateGroovy가 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() {
       //....
    }
}

그 작은 변화 후에 모든 것이 작동했습니다.

언급 할 가치가있는 한 가지가 있습니다. 내가 작동하게 한 후에 나는이 필드를 만들고 privatesetter 메서드에서 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를 사용하는 경우이를 염두에 두십시오.


15

구성에서 맵을 검색하려면 구성 클래스가 필요합니다. 불행히도 @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

당기위한 솔루션 사용 @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 문자열의"(따옴표)에 대한 이스케이프 문자입니다.

  • @Value에 의해 Map으로 변환 될 yaml의 {key : value} JSON

  • # {} SpEL 표현이며 @Value에서 json int Map 또는 Array / list Reference 를 변환하는 데 사용할 수 있습니다.

스프링 부트 프로젝트에서 테스트 됨


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

하지만 위키에 대한 링크는 가치가 있습니다. 설명은 github.com/spring-projects/spring-boot/wiki/…에 있습니다.
dschulten

1

추가 구조를 피하려면 더 간단하게 만들 수 있습니다.

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.