Spring@PropertySource使用YAML

Spring@PropertySource使用YAML,spring,spring-boot,Spring,Spring Boot,Spring Boot允许我们用YAML等价物替换应用程序.properties文件。然而,我的测试似乎遇到了障碍。如果我注释我的TestConfiguration(一个简单的Java配置),它需要一个属性文件 例如,这不起作用: @PropertySource(value=“classpath:application test.yml”) 如果我的YAML文件中有此项: db: url: jdbc:oracle:thin:@pathToMyDb username: someUser

Spring Boot允许我们用YAML等价物替换
应用程序.properties
文件。然而,我的测试似乎遇到了障碍。如果我注释我的
TestConfiguration
(一个简单的Java配置),它需要一个属性文件

例如,这不起作用:
@PropertySource(value=“classpath:application test.yml”)

如果我的YAML文件中有此项:

db:
  url: jdbc:oracle:thin:@pathToMyDb
  username: someUser
  password: fakePassword
我会利用这些价值观,比如:

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

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

}
@Value(“${db.username}”)字符串用户名
但是,我最终遇到了这样一个错误:

无法解析字符串值“${db.username}”中的占位符“db.username”

如何在测试中利用YAML的优点?

加载YAML属性的方法,IMHO可以通过两种方式完成:

a。您可以将配置放在一个标准位置-
application.yml
在类路径根中-通常
src/main/resources
,这个yaml属性应该由Spring boot使用您提到的平坦路径名自动加载

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引导。

@PropertySource
仅支持属性文件(这是Spring的限制,而不是引导本身)。您可以随意打开一个功能请求单。

Spring boot为此提供了一个帮助程序,只需添加

@ContextConfiguration(initializers = ConfigFileApplicationContextInitializer.class)
在测试类或抽象测试超类的顶部

编辑:这是我五年前写的答案。它不适用于最新版本的Spring Boot。这就是我现在所做的(如有必要,请将Kotlin翻译成Java):

添加到顶部,然后

    @Configuration
    open class TestConfig {

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

这是因为您没有配置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
}

通过使用
@ActiveProfiles(“test”)
并将application-test.yml文件添加到src/test/resources,我找到了一个解决方法

结果是这样的:

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

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

}

文件application-test.yml只包含我想从application.yml重写的属性(可以在src/main/resources中找到)。

我需要将一些属性读入我的代码中,这适用于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);
}

如前所述,
@PropertySource
不加载yaml文件。作为一种解决方法,您可以自己加载文件,并将加载的属性添加到
环境中

实现
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);
    }
  }
}

另一个选项是通过
@TestPropertySource
设置
spring.config.location

@TestPropertySource(properties={“spring.config.location=classpath:”}

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

详细资料

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

默认情况下,此注释将

…首先尝试从任何内部类加载
@Configuration
,如果失败,它将搜索主
@springbootplication

但如果需要,您可以指定其他配置类

对于这种特殊情况,您可以将
@SpringBootTest
@ActiveProfiles(“test”)
结合起来,如果YAML配置遵循正常的启动命名标准(即
应用程序测试.yml
),Spring将选择YAML配置


注意:
SpringRunner.class
SpringJUnit4ClassRunner.class
的新名称,用于在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;
    }
}
@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;
    }   

}
2) 如下所示配置JavaPOJO对象

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

@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);
    }
}
公共类YamlPropertyLoaderFactory扩展了DefaultPropertySourceFactory{
@凌驾
公共属性源createPropertySource(字符串名称,EncodedResource资源)引发IOException{
if(资源==null){
返回super.createPropertySource(名称、资源);
}
返回新的YamlPropertySourceLoader().load(resource.getResource().getFilename(),resource.getResource(),null);
}
}

启发,我遇到了一个特殊情况,由于自定义文件属性命名,我无法加载@ConfigurationProperties类。最后,唯一有效的方法是(感谢@Mateusz Balbus):

导入java.io.File;
导入java.io.FileInputStream;
导入java.io.IOException;
导入java.util.List;
导入org.apache.commons.io.IOUtils;
导入org.junit.Test;
导入org.junit.runner.RunWith;
导入org.springframework.beans.factory.annotation.Autowired;
导入org.springframework.beans.factory.annotation.Qualifier;
导入org.springframework.boot.context.properties.bind.Bindable;
导入org.springframework.boot.context.properties.bind.Binder;
导入org.springframework.boot.env.YamlPropertySourceLoader;
导入组织
@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);
    }

}
@ImportResource({"classpath: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>
db:
   url: jdbc:oracle:thin:@pathToMyDb
   username: someUser
   password: fakePassword