YAML을 사용하는 Spring @PropertySource


107

Spring Boot를 사용하면 application.properties 파일을 YAML에 해당하는 파일로 바꿀 수 있습니다. 그러나 나는 내 시험에 걸림돌을 친 것 같다. 내 TestConfiguration(간단한 Java 구성)에 주석을 달면 속성 파일이 필요합니다.

예를 들어 이것은 작동하지 않습니다. @PropertySource(value = "classpath:application-test.yml")

내 YAML 파일에 다음이있는 경우 :

db:
  url: jdbc:oracle:thin:@pathToMyDb
  username: someUser
  password: fakePassword

그리고 나는 다음과 같이 그 가치를 활용할 것입니다.

@Value("${db.username}") String username

그러나 나는 다음과 같은 오류로 끝납니다.

Could not resolve placeholder 'db.username' in string value "${db.username}"

내 테스트에서도 YAML의 장점을 어떻게 활용할 수 있습니까?


"작동하지 않음"을 정의하십시오. 예외 / 오류 / 경고는 무엇입니까?
Emerson Farrugia 2014 년

Spring Boot는 YAML 파일을 평면화하여 점 표기법이있는 속성 파일로 나타납니다. 그 평탄화는 일어나지 않습니다.
checketts 2014 년

그리고 확인하기 위해 이것은 테스트가 아닌 코드에서 작동합니까?
Emerson Farrugia 2014 년

1
예. 다음은 projects.spring.io/spring-boot/docs/spring-boot-actuator/…를 설명하는 문서 이며 페이지 아래로 내려가는 방법은 'YAML 객체가 마침표 구분자를 사용하여 평면화된다는 점에 유의하세요.'
checketts 2014 년

9
SpingBoot는 PropertySource로 YAML을로드 할 수 없다고 말했습니다. 24.6.4 YAML 단점 YAML 파일은 @PropertySource 주석을 통해로드 할 수 없습니다. 따라서 이러한 방식으로 값을로드해야하는 경우 속성 파일을 사용해야합니다.
Lex Pro

답변:


55

Spring-boot에는 이에 대한 도우미가 있습니다.

@ContextConfiguration(initializers = ConfigFileApplicationContextInitializer.class)

테스트 클래스 또는 추상 테스트 수퍼 클래스의 맨 위에 있습니다.

편집 : 5 년 전에이 답변을 썼습니다. 최신 버전의 Spring Boot에서는 작동하지 않습니다. 이것이 제가 지금하는 일입니다 (필요한 경우 Kotlin을 Java로 번역하십시오).

@TestPropertySource(locations=["classpath:application.yml"])
@ContextConfiguration(
        initializers=[ConfigFileApplicationContextInitializer::class]
)

상단에 추가 된 다음

    @Configuration
    open class TestConfig {

        @Bean
        open fun propertiesResolver(): PropertySourcesPlaceholderConfigurer {
            return PropertySourcesPlaceholderConfigurer()
        }
    }

맥락에.


3
PropertySourcesPlaceholderConfigurer를 잊지 마세요
Kalpesh Soni

@KalpeshSoni 실제로 구성자 없이는 작동하지 않습니다.
Ola Sundell

대신 @SpringJunitConfig에 이니셜 라이저를 추가해야했습니다@SpringJUnitConfig(value = {...}, initializers = {ConfigFileApplicationContextInitializer.class})
Tomas F

1
@ Jan Galinski는 내 대답을 시도해 볼 수 있으며 사용하기 쉽고 내 제품 환경에서 잘 작동합니다. stackoverflow.com/questions/21271468/...
Forest10에게

59

언급했듯이 @PropertySourceyaml 파일을로드하지 않습니다. 해결 방법으로 파일을 직접로드하고로드 된 속성을 Environment.

구현 ApplicationContextInitializer:

public class YamlFileApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
  @Override
  public void initialize(ConfigurableApplicationContext applicationContext) {
    try {
        Resource resource = applicationContext.getResource("classpath:file.yml");
        YamlPropertySourceLoader sourceLoader = new YamlPropertySourceLoader();
        PropertySource<?> yamlTestProperties = sourceLoader.load("yamlTestProperties", resource, null);
        applicationContext.getEnvironment().getPropertySources().addFirst(yamlTestProperties);
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
  }
}

테스트에 이니셜 라이저를 추가합니다.

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class, initializers = YamlFileApplicationContextInitializer.class)
public class SimpleTest {
  @Test
  public test(){
    // test your properties
  }
}

실제로 이것이 최선의 답변이어야합니다. 감사합니다!
Adelin

Mateusz, YamlFileApplicationContextInitializer테스트 케이스별로 YAML 위치가 정의 된 클래스 에 대한 답변을 게시했습니다 . 흥미 롭다고 생각되면 답변에 병합 해 주시면 삭제하겠습니다. 내 대답 아래에 댓글로 알려주십시오.
Michal Foksa 2018

예, 이것이 최고의 답변입니다
Richard HM

34

@PropertySourcefactory인수 로 구성 할 수 있습니다 . 따라서 다음과 같이 할 수 있습니다.

@PropertySource(value = "classpath:application-test.yml", factory = YamlPropertyLoaderFactory.class)

YamlPropertyLoaderFactory사용자 정의 속성 로더는 어디에 있습니까?

public class YamlPropertyLoaderFactory extends DefaultPropertySourceFactory {
    @Override
    public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
        if (resource == null){
            return super.createPropertySource(name, resource);
        }

        return new YamlPropertySourceLoader().load(resource.getResource().getFilename(), resource.getResource(), null);
    }
}

https://stackoverflow.com/a/45882447/4527110에서 영감을 얻었습니다.


2
이 기본 yaml 구문 분석 IllegalStateException은 파일이 적절한 대신 존재하지 않을 때 발생합니다. 따라서이 FileNotFoundException작업을 수행 @PropertySource(..., ignoreResourceNotFound = true)하려면이 경우를 포착하고 처리해야합니다. try { return new YamlPropertySourceLoader().load(resource.getResource().getFilename(), resource.getResource(), null); } catch (IllegalStateException e) { throw (IOException) e.getCause(); }
Christian Opitz

2
특정 프로필에 대한 속성을 가져와야하는 경우 YamlPropertySourceLoader.load ()의 세 번째 매개 변수는 프로필 이름입니다. YamlPropertySourceLoader.load ()가 단일 속성 소스가 아닌 목록을 반환하도록 변경되었습니다. 다음의 추가 정보 stackoverflow.com/a/53697551/10668441은
pcoates

1
이것은 지금까지 가장 깨끗한 접근 방식입니다.
Michal Foksa 19

7
저에게는 다음과 같이 약간의 수정이 필요했습니다.CompositePropertySource propertySource = new CompositePropertySource(name); new YamlPropertySourceLoader().load(resource.getResource().getFilename(), resource.getResource()).stream().forEach(propertySource::addPropertySource); return propertySource;
xorcus

28

@PropertySource속성 파일 만 지원합니다 (부트 자체가 아닌 Spring의 제한 사항입니다). JIRA에서 기능 요청 티켓 자유롭게여십시오 .


yaml 리스너를 재사용하거나 테스트 구성으로 전달할 수있는 환경에서 yaml을 수동으로로드하는 방법이 있었으면합니다.
checketts 2014 년

10
를 작성 ApplicationContextInitializer하여 테스트 구성에 추가 할 수 있다고 가정합니다 (A를 사용 YamlPropertySourceLoader하여 Environment). 개인적 @PropertySource으로이 동작을 기본적으로 지원 한다면 선호합니다 .
Dave Syer 2014 년

이것이 여전히 사실입니까? '@PropertySource'는 YAML을 지원하지 않습니까?
domi

1
stackoverflow.com/questions/21271468/… 이것을 사용하면 @PropertySource는 속성 파일 만 지원합니다
Forest10

이 6 년 된 게시물로 내 문제를 해결하는 데 충격을 받았습니다.
진 권

20

또 다른 옵션은 다음을 spring.config.location통해 설정하는 것입니다 @TestPropertySource.

@TestPropertySource(properties = { "spring.config.location = classpath:<path-to-your-yml-file>" }

3
다음 줄로 입력을 매개 변수화했습니다. @TestPropertySource(properties = {"spring.config.location=classpath:application-${test.env}.yml" }) IMO yours가 가장 좋은 답변입니다.
leventunver

1
테스트를위한 좋은 아이디어와 매우 미니멀합니다. 감사합니다! 추가하기 만하면 다음과 같이 여러 구성 파일을 포함 할 수 있습니다.@TestPropertySource(properties = {"spring.config.location=classpath:application-config.yml,classpath:test-config.yml,..." })
stx

1
이것은 지금까지 최고의 답변입니다! 당신이 할 필요가 있습니다 @SpringBootTest주석
Mistriel

마법처럼 작동합니다!
user1079877

19

Spring Boot 1.4부터는 Spring @SpringBootTestBoot 지원을 사용하여 통합 테스트를 부트 스트랩하여 새 주석을 사용하여 이를보다 쉽게 ​​수행 할 수 있습니다 (일반적으로 통합 테스트 설정을 단순화).

봄 블로그 에 대한 세부 사항 .

내가 말할 수있는 한, 이것은 클래스 경로에서 YAML 구성을 자동으로 선택하는 것을 포함하여 프로덕션 코드 에서처럼 Spring Boot의 외부화 된 구성 장점 의 모든 이점을 얻을 수 있음을 의미합니다 .

기본적으로이 주석은

... 먼저 @Configuration내부 클래스에서 로드 를 시도 하고 실패하면 기본 @SpringBootApplication클래스를 검색합니다 .

그러나 필요한 경우 다른 구성 클래스를 지정할 수 있습니다.

이 특별한 경우 @SpringBootTest와 결합 할 수 @ActiveProfiles( "test" )있으며 Spring은 YAML 구성을 선택합니다 application-test.yml.

@RunWith( SpringRunner.class )
@SpringBootTest
@ActiveProfiles( "test" )
public class SpringBootITest {

    @Value("${db.username}")
    private String username;

    @Autowired
    private MyBean myBean;

    ...

}

참고 : SpringRunner.class의 새 이름입니다.SpringJUnit4ClassRunner.class


1
:) @ActiveProfiles를 사용하는 것이 유일한 옵션입니다. 감사!
zcourts

10

yaml 속성, IMHO를로드하는 방법은 두 가지 방법으로 수행 할 수 있습니다.

ㅏ. application.yml일반적으로 클래스 경로 루트 의 표준 위치에 구성을 배치 할 수 src/main/resources있으며이 yaml 속성은 언급 한 평면화 된 경로 이름으로 Spring 부트에 의해 자동으로로드됩니다.

비. 두 번째 접근 방식은 좀 더 광범위합니다. 기본적으로 다음과 같이 속성을 보유 할 클래스를 정의합니다.

@ConfigurationProperties(path="classpath:/appprops.yml", name="db")
public class DbProperties {
    private String url;
    private String username;
    private String password;
...
}

따라서 본질적으로 이것은 yaml 파일을로드하고 "db"의 루트 요소를 기반으로 DbProperties 클래스를 채우는 것입니다.

이제 모든 클래스에서 사용하려면 다음을 수행해야합니다.

@EnableConfigurationProperties(DbProperties.class)
public class PropertiesUsingService {

    @Autowired private DbProperties dbProperties;

}

이러한 접근 방식 중 하나는 Spring-boot를 사용하여 깔끔하게 작동합니다.


클래스 경로에 snakeyml이 있는지 확인하고 위의 내용이 작동해야합니다.
hoserdude 2014 년

3
요즘 (이 질문이 요청 된 당시는 아니었지만) snakeyaml는에서 전 이적 종속성으로 가져 오므로 다른 버전을 사용하려는 뿌리 깊은 충동이없는 한 또는 spring-boot-starter에 추가 할 필요가 없습니다 . :)pom.xmlbuild.gradle
Steve

2
그것은 이제 locations,하지 path, 그리고이 ConfigFileApplicationContextInitializer또한 필요합니다.
OrangeDog

3

@ActiveProfiles("test")application-test.yml 파일을 사용 하고 src / test / resources에 추가 하여 해결 방법을 찾았습니다 .

결과는 다음과 같습니다.

@SpringApplicationConfiguration(classes = Application.class, initializers = ConfigFileApplicationContextInitializer.class)
@ActiveProfiles("test")
public abstract class AbstractIntegrationTest extends AbstractTransactionalJUnit4SpringContextTests {

}

application-test.yml 파일에는 내가 application.yml (src / main / resources에서 찾을 수 있음)에서 재정의하려는 속성 만 포함되어 있습니다.


이것이 제가 사용하려고했던 것입니다. 몇 가지 이유 때문에 작업 (봄 부팅 1.3.3) 내가 사용하지 않는 @Value("${my.property}")하지만 사용하는 경우 그것을 잘 작동합니다 environment.getProperty("my.property").
martin-g

1

snakeyml을 구성하지 않았기 때문입니다. 봄 부팅은 @EnableAutoConfiguration 기능과 함께 제공됩니다. 이 주석을 호출 할 때도 snakeyml 구성이 있습니다.

이게 내 방법이야:

@Configuration
@EnableAutoConfiguration
public class AppContextTest {
}

내 테스트는 다음과 같습니다.

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(
        classes = {
                AppContextTest.class,
                JaxbConfiguration.class,
        }
)

public class JaxbTest {
//tests are ommited
}

0

내 코드에 몇 가지 속성을 읽어야했는데 이것은 spring-boot 1.3.0에서 작동합니다 .RELEASE

@Autowired
private ConfigurableListableBeanFactory beanFactory;

// access a properties.yml file like properties
@Bean
public PropertySource properties() {
    PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
    YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean();
    yaml.setResources(new ClassPathResource("properties.yml"));
    propertySourcesPlaceholderConfigurer.setProperties(yaml.getObject());
    // properties need to be processed by beanfactory to be accessible after
    propertySourcesPlaceholderConfigurer.postProcessBeanFactory(beanFactory);
    return propertySourcesPlaceholderConfigurer.getAppliedPropertySources().get(PropertySourcesPlaceholderConfigurer.LOCAL_PROPERTIES_PROPERTY_SOURCE_NAME);
}

0

Spring Boot에서 다중 프로파일 구성으로 사용자 정의 yml 파일을로드합니다.

1) 다음과 같이 SpringBootApplication 시작과 함께 속성 빈을 추가합니다.

@SpringBootApplication
@ComponentScan({"com.example.as.*"})
public class TestApplication {

    public static void main(String[] args) {
        SpringApplication.run(TestApplication.class, args);
    }

    @Bean
    @Profile("dev")
    public PropertySourcesPlaceholderConfigurer propertiesStage() {
        return properties("dev");
    }

    @Bean
    @Profile("stage")
    public PropertySourcesPlaceholderConfigurer propertiesDev() {
        return properties("stage");
    }

    @Bean
    @Profile("default")
    public PropertySourcesPlaceholderConfigurer propertiesDefault() {
        return properties("default");

    }
   /**
    * Update custom specific yml file with profile configuration.
    * @param profile
    * @return
    */
    public static PropertySourcesPlaceholderConfigurer properties(String profile) {
       PropertySourcesPlaceholderConfigurer propertyConfig = null;
       YamlPropertiesFactoryBean yaml  = null;

       propertyConfig  = new PropertySourcesPlaceholderConfigurer();
       yaml = new YamlPropertiesFactoryBean();
       yaml.setDocumentMatchers(new SpringProfileDocumentMatcher(profile));// load profile filter.
       yaml.setResources(new ClassPathResource("env_config/test-service-config.yml"));
       propertyConfig.setProperties(yaml.getObject());
       return propertyConfig;
    }
}

2) 다음과 같이 Java pojo 객체를 구성하십시오.

@Component
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(Include.NON_NULL)
@ConfigurationProperties(prefix = "test-service")
public class TestConfig {

    @JsonProperty("id") 
    private  String id;

    @JsonProperty("name")
    private String name;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }   

}

3) 사용자 정의 yml을 생성하고 다음과 같이 리소스 경로 아래에 배치합니다. YML 파일 이름 : test-service-config.yml

예를 들어 yml 파일의 구성.

test-service: 
    id: default_id
    name: Default application config
---
spring:
  profiles: dev

test-service: 
  id: dev_id
  name: dev application config

--- 
spring:
  profiles: stage

test-service: 
  id: stage_id
  name: stage application config

0

사용자 지정 파일 속성 이름 지정으로 인해 @ConfigurationProperties 클래스를로드 할 수없는 특정 상황에있었습니다. 결국 효과가 있었던 유일한 것은 (@Mateusz Balbus에게 감사드립니다) :

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.List;

import org.apache.commons.io.IOUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.env.YamlPropertySourceLoader;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.Resource;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {MyTest.ContextConfiguration.class})
public class MyTest {

    @TestConfiguration
    public static class ContextConfiguration {

        @Autowired
        ApplicationContext applicationContext;

        @Bean
        public ConfigurationPropertiesBean myConfigurationPropertiesBean() throws IOException {
            Resource resource = applicationContext.getResource("classpath:my-properties-file.yml");

            YamlPropertySourceLoader sourceLoader = new YamlPropertySourceLoader();
            List<PropertySource<?>> loadedSources = sourceLoader.load("yamlTestProperties", resource);
            PropertySource<?> yamlTestProperties = loadedSources.get(0);
            ConfigurableEnvironment configurableEnvironment = (ConfigurableEnvironment)applicationContext.getEnvironment();
            configurableEnvironment.getPropertySources().addFirst(yamlTestProperties);

            Binder binder = Binder.get(applicationContext.getEnvironment());
            ConfigurationPropertiesBean configurationPropertiesBean = binder.bind("my-properties-file-prefix", Bindable.of(ConfigurationPropertiesBean.class)).get();
            return configurationPropertiesBean;
        }

    }

    @Autowired
    ConfigurationPropertiesBean configurationPropertiesBean;

    @Test
    public void test() {

        configurationPropertiesBean.getMyProperty();

    }

}

0
<dependency>
  <groupId>com.github.yingzhuo</groupId>
  <artifactId>spring-boot-stater-env</artifactId>
  <version>0.0.3</version>
</dependency>

내 라이브러리 사용을 환영합니다. 이제 yaml , toml , hocon 이 지원됩니다.

출처 : github.com


0

이것은 원래 질문에 대한 답이 아니라 테스트에서 다른 구성이 필요한 경우에 대한 대체 솔루션입니다.

대신 @PropertySource당신이 사용할 수 있습니다 -Dspring.config.additional-location=classpath:application-tests.yml.

접미사 tests는 프로필을 의미하지 않습니다.

하나의 YAML 파일에서 서로 상속 할 수있는 여러 프로필을 지정할 수 있습니다. 자세한 내용은 여기를 참조하세요- 여러 Spring 프로필에 대한 속성 해결 (yaml 구성)

그런 다음 테스트에서 활성 프로필 (사용 )이에서 속성을 재정의 (일부, 모두 재정의 할 필요가 없음 @ActiveProfiles("profile1,profile2")) 할 profile1,profile2위치 profile2를 지정할 수 있습니다 profile1.


0

나열된 모든 질문을 시도했지만 모두 내 작업에는 작동하지 않습니다. 일부 단위 테스트에 특정 yaml 파일 사용. 제 경우에는 다음과 같이 작동합니다.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(initializers = {ConfigFileApplicationContextInitializer.class})
@TestPropertySource(properties = {"spring.config.location=file:../path/to/specific/config/application.yml"})
public class SomeTest {


    @Value("${my.property.value:#{null}}")
    private String value;

    @Test
    public void test() {
        System.out.println("value = " + value);
    }

}

-6

YamlPropertyLoaderFactory 또는 YamlFileApplicationContextInitializer와 같이 추가 할 필요가 없습니다. 아이디어를 전환해야합니다. 일반적인 봄 프로젝트와 같습니다. Java 구성을 사용하지 않습니다. * .xml 만

다음과 같이하세요:

applicationContext.xml을 다음과 같이 추가하십시오.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd"
       default-autowire="byName">

    <context:property-placeholder location="classpath*:*.yml"/>
</beans>

그런 다음 추가

@ImportResource({"classpath:applicationContext.xml"})

귀하의 ApplicationMainClass.

이것은 application-test.yml을 스캔하는 데 도움이 될 수 있습니다.

db:
  url: jdbc:oracle:thin:@pathToMyDb
  username: someUser
  password: fakePassword

질문은 yaml과 관련이 있습니다 (IMHO는 좋은 구성 방법 임)
aldebaran-ms
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.