How to configure multiple config files for a spring boot profile?

Bharathi Kannan
2 min readMay 14, 2019

Spring boot doesn’t support loading multiple YAML configuration files for a profile. I tried solving this problem using @PropertySource annotation. But PropertySource doesn’t support loading YAML files and also it is restricted for a single resource. Using a monolithic configuration file for a profile makes it complex for future changes. Quick info about profile from spring doc

Bean definition profiles provide a mechanism in the core container that allows for registration of different beans in different environments. The word, “environment,” can mean different things to different users, and this feature can help with many use cases, including:@PropertySource(factory = YamlPropertySourceFactory.class, value = “”, name=”props”)`

However, if you take a closer look at @PropertySource` annotation it supports adding custom PropertySourceFactory . Let’s jump into the simple implementation


import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.core.io.support.PropertySourceFactory;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Properties;

/**
*
@author bk
*/
@Slf4j
public class YamlPropertySourceFactory implements PropertySourceFactory {

@Override
public PropertySource<?> createPropertySource(String s, EncodedResource resource)
throws IOException {
Properties props = new Properties();
Resource defaultResource = resource.getResource();

if(defaultResource.exists()) {
log.info("Adding default properties. File: {}", defaultResource.getFilename());
props.putAll(this.loadYamlAsProperties(defaultResource));
}
String profile = getActiveProfile();
log.info("### Loading profile properties ## : {}", profile);

FileSystemResource[] configs = findAll(profile);

for(FileSystemResource config: configs) {
props.putAll(loadYamlAsProperties(config));
}

return new PropertiesPropertySource(s, props);
}

private Properties loadYamlAsProperties(Resource resource) {
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
factory.setResources(resource);
factory.afterPropertiesSet();
return factory.getObject();
}


private String getActiveProfile() throws IOException {
ClassPathResource applicationProperties = new ClassPathResource("application.properties");
Properties properties = new Properties();
properties.load(applicationProperties.getInputStream());
return properties.getProperty("spring.profiles.active");
}

private FileSystemResource[] findAll(String profile) throws IOException {
ClassPathResource resource = new ClassPathResource("application.properties");
String resourceDir = resource.getFile().getParentFile().getAbsolutePath();
File file = new File(resourceDir);

return Arrays.asList(file.listFiles(f -> f.getName().contains("application-" + profile+"-"))).stream().map(f -> new FileSystemResource(f))
.toArray(FileSystemResource[]::new);
}

}

@PropertySource loads even before Environment` bean, so getting a current active profile from Environment` won’t work. So, we are extracting current active profile from application.properties`

Let’s say, the current profile is dev. It will load all the configuration files for the particular file from src/main/resources . Configuration files should follow application-{profile}-{category}.yml` format.

Enjoy using it with @Value, @ConfigurationProperties and …

--

--