programing

YAML을 사용한 Spring @PropertySource

newnotes 2023. 3. 6. 21:27
반응형

YAML을 사용한 Spring @PropertySource

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의 장점을 활용하는 방법은 무엇입니까?

스프링 부트에는 이를 위한 도우미가 있습니다. 추가만 하면 됩니다.

@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()
        }
    }

문맥에 맞게.

말씀드린 바와 같이@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
  }
}

@PropertySource설정할 수 있다factory논쟁.다음과 같은 작업을 수행할 수 있습니다.

@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에서 영감을 얻음

또 다른 옵션은 다음과 같습니다.spring.config.location통해.@TestPropertySource:

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

@PropertySource는 속성 파일만 지원합니다(스프링으로부터의 제한이며 부트 자체는 지원되지 않습니다).기능요청 티켓은 JIRA에서 자유롭게 오픈할 수 있습니다.

Spring Boot 1.4 이후로는@SpringBootTestSpring Boot 지원을 사용하여 통합 테스트를 부트스트래핑함으로써 이를 보다 쉽게 달성할 수 있습니다(또한 일반적으로 통합 테스트 셋업을 단순화할 수 있습니다).

자세한 내용은 Spring Blog.

즉, 클래스 패스에서 자동으로 YAML Config를 선택하는 등 실제 코드에서와 마찬가지로 Spring Boot의 외부화된 Configuration의 장점을 모두 누릴 수 있습니다.

기본적으로 이 주석은

...첫 번째 로드 시도@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

yaml 속성을 로드하는 접근법 IMHO는 두 가지 방법으로 수행할 수 있습니다.

a. 설정을 표준 위치에 배치할 수 있습니다.application.ymlroot - " " " " - " "src/main/resources그리고 이 yaml 속성은 당신이 말한 평탄한 경로 이름으로 스프링 부트에 의해 자동으로 로드됩니다.

b. 두 번째 접근법은 조금 더 광범위합니다. 기본적으로 다음과 같이 속성을 유지하는 클래스를 정의합니다.

@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 2.4.0 이후 다음과 같이 Config Data Application Context Initializer를 사용할 수 있습니다.

@SpringJUnitConfig(
    classes = { UserAccountPropertiesTest.TestConfig.class },
    initializers = { ConfigDataApplicationContextInitializer.class }
)
class UserAccountPropertiesTest {

    @Configuration
    @EnableConfigurationProperties(UserAccountProperties.class)
    static class TestConfig { }

    @Autowired
    UserAccountProperties userAccountProperties;

    @Test
    void getAccessTokenExpireIn() {
       assertThat(userAccountProperties.getAccessTokenExpireIn()).isEqualTo(120);
    }

    @Test
    void getRefreshTokenExpireIn() {
        assertThat(userAccountProperties.getRefreshTokenExpireIn()).isEqualTo(604800);
    }
}

참고 항목: https://www.baeldung.com/spring-boot-testing-configurationproperties#YAML-binding

를 사용하여 회피책을 찾았습니다.@ActiveProfiles("test")application-test.yml 에 추가합니다.

결국 다음과 같이 되었습니다.

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

}

application-test.yml 파일에는 application.yml(src/main/resources에 있음)에서 덮어쓸 속성만 포함되어 있습니다.

기재되어 있는 모든 질문을 시험해 보았습니다만, 특정의 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);
    }

}

snakeyml을 설정하지 않았기 때문입니다.스프링 부트는 @Enable과 함께 제공됩니다.자동 설정 기능.이 주석을 호출하면 snakyml 설정도 있습니다.

이게 내 방식이야

@Configuration
@EnableAutoConfiguration
public class AppContextTest {
}

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

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

public class JaxbTest {
//tests are ommited
}

코드의 속성을 읽어낼 필요가 있었습니다만, 이것은 spring-boot 1.3.0에서 동작합니다.풀어주다

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

Spring Boot에서 여러 프로파일 설정을 가진 커스텀 yml 파일을 로드하고 있습니다.

1) 다음과 같이 Spring Boot Application 부팅 시 속성 bean을 추가합니다.

@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 파일의 Config.

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
<dependency>
  <groupId>com.github.yingzhuo</groupId>
  <artifactId>spring-boot-stater-env</artifactId>
  <version>0.0.3</version>
</dependency>

내 도서관을 이용하게 된 것을 환영합니다.이제 yaml, toml, hocon이 지원됩니다.

출처 : github.com

이는 원래 질문에 대한 답변이 아니라 테스트에서 다른 구성을 사용해야 하는 경우의 대체 솔루션입니다.

@PropertySource 하면 .-Dspring.config.additional-location=classpath:application-tests.yml.

서픽스 「」, 「」에 주의해 주세요tests

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

다음 테스트에서사용하는 프로파일)을할 수 .@ActiveProfiles("profile1,profile2")는 )의 개요profile1,profile2서 ''는profile2 (을 덮어쓸 의덮어씁니다(일부 덮어쓸 필요는 없습니다).profile1.

여기에 이미지 설명 입력

프로젝트 데모 URL:https://github.com/Forest10/spring-boot-family/tree/spring-boot-with-yml

나는 이 대답을 내 prod env에서 실행해!!!반대하시는 분은 먼저 테스트해 주세요!!!

Yaml Property Loader Factory 나 Yaml File Application Context Initializer 와 같이 추가할 필요가 없습니다.당신은 당신의 생각을 바꿔야 한다

다음의 순서에 따릅니다.

application Context.xml을 다음과 같이 추가합니다.

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

ApplicationMainClass로 이동합니다.

applicationContext.xml은 다음과 같이 써야 합니다.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:context="http://www.springframework.org/schema/context"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  default-autowire="byName"
  xmlns="http://www.springframework.org/schema/beans"
  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">

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

이를 통해 application-test.yml을 스캔할 수 있습니다.

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


Mateusz Balbus의 답변을 강화합니다.

★★YamlFileApplicationContextInitializer클래스. 여기서 YAML 로케이션은 테스트클래스별로 정의됩니다.유감스럽게도 테스트당 동작하지 않습니다.

public abstract class YamlFileApplicationContextInitializer
  implements ApplicationContextInitializer<ConfigurableApplicationContext> {

  /***
   * Return location of a YAML file, e.g.: classpath:file.yml
   *
   * @return YAML file location
   */
  protected abstract String getResourceLocation();

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

사용방법:

.YamlFileApplicationContextInitializer되어 있다getResourceLocation()를 메서드에 합니다.@SpringApplicationConfiguration석입니니다다

이렇게 하면 시험 수업 자체를 만드는 것이 가장 쉽다.

@RunWith(SpringRunner.class)
@SpringApplicationConfiguration(classes = Application.class, initializers = SimpleTest.class)
public class SimpleTest extends YamlFileApplicationContextInitializer {

  @Override
  protected String getResourceLocation() {
    return "classpath:test_specific.yml";
  }

  @Test
  public test(){
    // test your properties
  }
}

개량된 버전이 YamlPropertyLoaderFactory 「」를 하고 있습니다.PropertySource.ignoreResourceNotFound 답변에 근거합니다.

자바:

public final class YamlPropertyLoaderFactory extends DefaultPropertySourceFactory {
    private final YamlPropertySourceLoader yamlPropertySourceLoader = new YamlPropertySourceLoader();

    @NotNull
    public PropertySource createPropertySource(
            @Nullable String name,
            @NotNull EncodedResource resource
    ) {
        try {
            String parsedName;
            if (name != null && !name.equals(""))
                parsedName = name;
            else parsedName = resource.getResource().getFilename();
            return yamlPropertySourceLoader.load(parsedName, resource.getResource()).get(0);
        } catch (Exception e) {
            Exception possibleFileNotFoundException = ExceptionUtils.throwableOfType(e, FileNotFoundException.class);
            throw possibleFileNotFoundException != null ? possibleFileNotFoundException : e;
        }
    }
}

// Usage
@PropertySource(
   value = {"file:./my-optional-config.yml"},
   factory = YamlPropertyLoaderFactory.class,
   ignoreResourceNotFound = true
)

코틀린:

class YamlPropertyLoaderFactory : DefaultPropertySourceFactory() {
    private val yamlPropertySourceLoader = YamlPropertySourceLoader()

    override fun createPropertySource(
        name: String?,
        resource: EncodedResource
    ): PropertySource<*> = try {
        (
            yamlPropertySourceLoader.load(
                if (name != null && name.isNotBlank()) name else resource.resource.filename,
                resource.resource
            )
            )[0]
    } catch (e: Exception) {
        throw ExceptionUtils.throwableOfType(e, FileNotFoundException::class.java) ?: e
    }
}

// Usage
@PropertySource(
    value = ["file:/my-optional-config.yml"],
    factory = YamlPropertyLoaderFactory::class,
    ignoreResourceNotFound = true
)

언급URL : https://stackoverflow.com/questions/21271468/spring-propertysource-using-yaml

반응형