Spring@PropertySource使用YAML
Spring Boot允许我们用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
应用程序.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