I'm trying to set up my profiles using YAML configuration files. Rather than having different @Configuration
classes annotated with @Profile
for different ones (or even a same @Configuration
class with different annotated @Beans
), i would like to have one @Configuration
class using Placeholder with YAML configuration file.
Taking a look at Spring boot documentation, YamlPropertiesFactoryBean javadoc and this Stackoverflow topic, i've came up with the following code:
A simple YAML file: application-default-config.yml
foo: myFoo
A @Configuration
file:
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.io.ClassPathResource;
@Configuration
public class ApplicationConfig {
@Bean
public static PropertySourcesPlaceholderConfigurer ymlProperties() {
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer =
new PropertySourcesPlaceholderConfigurer();
YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean();
yaml.setResources(new ClassPathResource("config/application-${spring.profiles.active:default}-config.yml"));
propertySourcesPlaceholderConfigurer.setProperties(yaml.getObject());
return propertySourcesPlaceholderConfigurer;
}
@Value("${foo}")
private String fooValue;
@Bean
public String fooValue() {
return fooValue;
}
}
A test file:
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = ApplicationConfig.class)
public class ApplicationConfigTest {
@Autowired
Environment environment;
@Autowired
String fooValue;
@Test
public void testFooViaEnvironment() {
Assert.assertEquals("myFoo", environment.getProperty("foo"));
}
@Test
public void testFooViaWiredValue() {
Assert.assertEquals("myFoo", fooValue);
}
}
And, finally, a main file:
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.io.ClassPathResource;
public class Application {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(ApplicationConfig.class);
YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean();
yaml.setResources(new ClassPathResource("config/application-${spring.profiles.active:default}-config.yml"));
ConfigurableEnvironment environment = ctx.getEnvironment();
environment.getPropertySources()
.addFirst(new PropertiesPropertySource("custom", yaml.getObject()));
ctx.refresh();
String fooViaWiredValue = (String) ctx.getBean("fooValue");
System.out.println(String.format("fooViaEnvironment=%s", environment.getProperty("foo")));
System.out.println(String.format("fooViaWiredValue=%s", fooViaWiredValue));
}
}
I've noticed that in my test class, i can access the 'foo' property via @Value
annotation but not via Environment
.
As well explained in some Stackoverflow topics like this and that, annotating a PropertySourcesPlaceholderConfigurer
as a @Bean
is not enough to tight it with the Environment
. It is necessary to do it programmatically as i did in Application
class. In contrast, annotated @Value
s are wired accordingly.
My questions are, in fact, about best practices working with Spring.
I would prefer using Environment
rather than @Value
annotation.
Is there any way to load my YAML configuration files into Spring application context inside a test environment without needing to do it programmatically as i did in Application
class ?
Is there any reason i should prefer using @Value
(or any other profile configuration) over Environment
+ YAML configuration files ? As i said, i would like to have a unique @Bean
using placeholders rather than multiples @Bean
s annotated with @Profile
s.