使用YAML的Spring @PropertySource


107

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优势?


定义“无效”。什么是例外/错误/警告?
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缺点无法通过@PropertySource批注加载YAML文件。因此,在需要以这种方式加载值的情况下,需要使用属性文件。
Lex Pro

Answers:


54

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

@ContextConfiguration(initializers = ConfigFileApplicationContextInitializer.class)

在测试类或抽象测试超类的顶部。

编辑:我五年前写了这个答案。它不适用于最新版本的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实际上,如果没有配置程序,它将无法正常工作。
奥拉·桑德尔

我必须将初始化程序添加到@SpringJunitConfig中@SpringJUnitConfig(value = {...}, initializers = {ConfigFileApplicationContextInitializer.class})
Tomas F

1
@Jan Galinski您可以尝试我的答案,它易于使用,并且在我的产品环境中很好用。stackoverflow.com/questions/21271468/...
Forest10

59

如前所述,它@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
  }
}

实际上,这应该是最好的答案,谢谢!
Adelin

Mateusz,我在YamlFileApplicationContextInitializer类中发布了答案,其中每个测试用例都定义了YAML位置。如果您认为这很有趣,请随时将其合并到您的答案中,我将删除我的。请在下面的评论中让我知道我的答案。
米哈尔·福克萨

是的,这是最好的答案
Richard HM

34

@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启发


2
IllegalStateException当文件不存在时,此基本的yaml解析会引发一个错误,而不是正确的文件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,

7
对我来说,它需要做如下修改:CompositePropertySource propertySource = new CompositePropertySource(name); new YamlPropertySourceLoader().load(resource.getResource().getFilename(), resource.getResource()).stream().forEach(propertySource::addPropertySource); return propertySource;
xorcus

28

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


我希望有一种方法可以重新使用yaml侦听器或在可以传递到测试配置的环境中手动加载yaml。
Checketts 2014年

10
我想您可以编写一个ApplicationContextInitializer并将其添加到测试配置中(只需使用YamlPropertySourceLoader来增强即可Environment)。就个人而言,我希望它能原生@PropertySource支持这种行为。
Dave Syer 2014年

还是这样吗?'@PropertySource'不支持YAML?
domi's

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您的IMO是您最好的选择。
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中,您可以使用@SpringBootTestSpring Boot支持通过引导集成测试来使用新的批注来更轻松地实现此目的(并总体上简化集成测试的设置)。

有关Spring Blog的详细信息。

据我所知,这意味着您可以像生产代码中那样获得Spring Boot的外部化配置优势,包括自动从类路径中获取YAML配置。

默认情况下,此注释将

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

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

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

@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 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-boot完全为您工作。


确保您的类路径中包含snakeyml,并且上面的方法应该起作用。
hoserdude 2014年

3
这些天(尽管不是在问这个问题的时候)snakeyaml被传递为依赖spring-boot-starter,因此,除非您根深蒂固地希望使用其他版本,否则不必将其添加到pom.xmlor中build.gradle。:)
史蒂夫

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中找到)。


这也是我试图使用的。出于某种原因,当我使用@Value("${my.property}")它时,它不起作用(Spring Boot 1.3.3),但如果使用,它就可以正常工作environment.getProperty("my.property")
martin-g

1

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

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启动添加属性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

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>

欢迎使用我的图书馆。现在支持yamltomlhocon

资料来源:github.com


0

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

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

请注意,后缀tests并不意味着配置文件...

在一个YAML文件中,您可以指定多个配置文件,这些配置文件可以相互继承,请在此处阅读更多内容- 解析多个Spring配置文件的属性(yaml配置)

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


-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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.