By default the @PropertySource
annotation isn’t usable with YAML files. This is also the standard answer given when asked on, for instance StackOverflow (even by me!).
But as of Spring 4.3 it’s possible to make it work. Spring 4.3 introduced the PropertySourceFactory
interface. The PropertySourceFactory
is a factory for a PropertySource
. The default implementation used is the DefaultPropertySourceFactory
, which creates ResourcePropertySource
instances.
Writing a custom implementation requires implementing a single method, createPropertySource. The custom implementation needs to do 2 things:
- Load the given resource into a java.util.Properties object
- Create a PropertySource wrapping the loaded properties
To load a YAML file Spring provides the YamlPropertiesFactoryBean
. This class will load 1 or more files and convert it into a java.util.Properties
object. Spring provides a [PropertiesPropertySource
] which wraps a java.util.Properties
object. Finally the name of the PropertySource is either given or derived. The derived name is the resource description, as mentioned in the contract.
package biz.deinum.blog.yaml;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Properties;
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.core.io.support.PropertySourceFactory;
import org.springframework.lang.Nullable;
public class YamlPropertySourceFactory implements PropertySourceFactory {
@Override
public PropertySource<?> createPropertySource(@Nullable String name, EncodedResource resource) throws IOException {
Properties propertiesFromYaml = loadYamlIntoProperties(resource);
String sourceName = name != null ? name : resource.getResource().getFilename();
return new PropertiesPropertySource(sourceName, propertiesFromYaml);
}
private Properties loadYamlIntoProperties(EncodedResource resource) throws FileNotFoundException {
try {
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
factory.setResources(resource.getResource());
factory.afterPropertiesSet();
return factory.getObject();
} catch (IllegalStateException e) {
// for ignoreResourceNotFound
Throwable cause = e.getCause();
if (cause instanceof FileNotFoundException)
throw (FileNotFoundException) e.getCause();
throw e;
}
}
}
NOTE: To load YAML it is required that SnakeYAML 1.18 or higher is on the classpath!
Next the YAML file, blog.yaml
which we want to load:
foo:
bar: baz
The @PropertySource
annotation has a factory
attribute. This is the attribute used to specify which PropertySourceFactory
to use. Here we give it the value YamlPropertySourceFactory.class
. The value attribute contains the name of the YAML resource to load.
package biz.deinum.blog.yaml;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.ConfigurableEnvironment;
@SpringBootApplication
@PropertySource(factory = YamlPropertySourceFactory.class, value = "classpath:blog.yaml")
public class YamlPropertysourceApplication {
public static void main(String[] args) {
ConfigurableApplicationContext ctx =
SpringApplication.run(YamlPropertysourceApplication.class, args);
ConfigurableEnvironment env = ctx.getEnvironment();
env.getPropertySources()
.forEach(ps -> System.out.println(ps.getName() + " : " + ps.getClass()));
System.out.println("Value of `foo.bar` = " + env.getProperty("foo.bar"));
}
}
NOTE: Although this sample uses Spring Boot this isn’t required it works with plain Spring (version 4.3 or up) as well.
When running this application it will
- Print the name and type of all available
PropertySource
s - Get the value from
foo.bar
which comes from theblog.yaml
file
The output should be something like the image below: