使用YAML的Spring @PropertySource

时间:2014-01-21 23:50:13

标签: spring spring-boot

Spring Boot允许我们用YAML等价物替换我们的application.properties文件。但是我的测试似乎遇到了麻烦。如果我注释我的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的优点?

19 个答案:

答案 0 :(得分:47)

如前所述,@PropertySource并未加载yaml文件。解决方法是自行加载文件并将加载的属性添加到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
  }
}

答案 1 :(得分:40)

Spring-boot有一个帮手,只需添加

@ContextConfiguration(initializers = ConfigFileApplicationContextInitializer.class)

位于测试类的顶部或抽象测试超类。

答案 2 :(得分:25)

@PropertySource仅支持属性文件(这是Spring的限制,而不是Boot本身)。随意打开功能请求票in JIRA

答案 3 :(得分:17)

从Spring Boot 1.4开始,您可以使用新的@SpringBootTest注释通过使用Spring Boot支持引导集成测试来更轻松地实现这一目标(并简化集成测试设置)。

Spring Blog的详细信息。

据我所知,这意味着您可以像生产代码一样获得Spring Boot externalized config goodness的所有好处,包括从类路径中自动获取YAML配置。

默认情况下,此注释将

  

...首先尝试从任何内部类加载@Configuration,如果失败,它将搜索您的主要@SpringBootApplication类。

但您可以根据需要指定其他配置类。

对于这种特殊情况,您可以将@SpringBootTest@ActiveProfiles( "test" )结合使用,如果Spring遵循正常的引导命名标准(即application-test.yml),Spring将选择您的YAML配置。

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

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

    @Autowired
    private MyBean myBean;

    ...

}

注意:SpringRunner.classSpringJUnit4ClassRunner.class

的新名称

答案 4 :(得分:13)

@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

的启发

答案 5 :(得分:10)

加载yaml属性的方法,恕我直言可以通过两种方式完成:

一个。您可以将配置放在类路径根目录中的标准位置 - 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,这些方法中的任何一个都应该干净利落。

答案 6 :(得分:9)

另一个选项是将spring.config.location设置为@TestPropertySource

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

答案 7 :(得分: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中找到)。

答案 8 :(得分:1)

从 Spring Boot 2.4.0 开始,您可以使用 ConfigDataApplicationContextInitializer 如下:

@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

答案 9 :(得分:0)

enter image description here

项目演示网址:https://github.com/Forest10/spring-boot-family/tree/spring-boot-with-yml

我在我的 prod 环境中运行这个答案!!!所以如果你反对这个答案。请先测试!!!

不需要像 YamlPropertyLoaderFactory 或 YamlFileApplicationContextInitializer 那样添加。你应该转换你的想法

请按照以下步骤操作:

只需添加 applicationContext.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


答案 10 :(得分: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);
    }

}

答案 11 :(得分:0)

这不是对原始问题的答案,而是需要在测试中具有不同配置的替代解决方案...

您可以使用@PropertySource代替-Dspring.config.additional-location=classpath:application-tests.yml

请注意,后缀tests并不意味着个人资料...

由于一个YAML文件可以指定多个配置文件,而这些配置文件可以相互继承,请在此处详细了解-Property resolving for multiple Spring profiles (yaml configuration)

然后,您可以在测试中指定(使用@ActiveProfiles("profile1,profile2")的活动配置文件是profile1,profile2,其中profile2将仅覆盖(某些不需要覆盖所有属性)属性来自profile1

答案 12 :(得分:0)

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

欢迎使用我的图书馆。现在支持 yaml toml hocon

来源:github.com

答案 13 :(得分: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();

    }

}

答案 14 :(得分:0)

您可以通过在@PropertySource属性中提供一个类来配置factory来加载YAML文件。我实现了一个版本,该版本也可以考虑您部署的活动配置文件:)。

https://medium.com/@james.tran/how-to-read-profile-based-yaml-configurations-with-propertysource-8131c16b3fc6

答案 15 :(得分:0)


无需添加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:application*.xml"})

到您的ApplicationMainClass

一切正常!

答案 16 :(得分:0)

在Spring Boot中加载带有多个配置文件配置的自定义yml文件。

1)使用SpringBootApplication启动添加属性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文件中配置。

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

答案 17 :(得分: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);
}

答案 18 :(得分:0)

这是因为你没有配置snakeyml。 spring boot附带@EnableAutoConfiguration功能。 当你调用这个注释时,也有snakeyml配置..

这是我的方式:

@Configuration
@EnableAutoConfiguration
public class AppContextTest {
}

这是我的测试:

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

public class JaxbTest {
//tests are ommited
}