I am working on a Spring application and I am realizing that I have an issue with the way I manage my properties. I use Spring environment profiles in order to load my properties and I've recently added more profiles which has made my properties files unmanagable.
The properties files are located in different folders within src/main/resources/META-INF/props/
, with eah folder matching a different Spring environment profile.
I have at least 5 profiles now which means I have 5 sub-folders each containing the properties files with the same names but with different values for only some keys.
Here is how it looks:
Here is how I've configured my PropertyPlaceholders:
@Configuration
public class PropertyPlaceholderConfiguration {
@Profile(Profiles.CLOUD)
static class cloudConfiguration {
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() throws IOException {
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
propertySourcesPlaceholderConfigurer.setIgnoreUnresolvablePlaceholders(Boolean.TRUE);
propertySourcesPlaceholderConfigurer.setLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:META-INF/props/cloud/*.properties"));
return propertySourcesPlaceholderConfigurer;
}
}
@Profile(Profiles.DEFAULT)
static class defaultConfiguration {
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() throws IOException {
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
propertySourcesPlaceholderConfigurer.setIgnoreUnresolvablePlaceholders(Boolean.TRUE);
propertySourcesPlaceholderConfigurer.setLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:META-INF/props/default/*.properties"));
return propertySourcesPlaceholderConfigurer;
}
}
@Profile(Profiles.TEST)
static class testConfiguration {
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() throws IOException {
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
propertySourcesPlaceholderConfigurer.setIgnoreUnresolvablePlaceholders(Boolean.TRUE);
propertySourcesPlaceholderConfigurer.setLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:META-INF/props/test/*.properties"));
return propertySourcesPlaceholderConfigurer;
}
}
@Profile(Profiles.DEV)
static class devConfiguration {
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() throws IOException {
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
propertySourcesPlaceholderConfigurer.setIgnoreUnresolvablePlaceholders(Boolean.TRUE);
propertySourcesPlaceholderConfigurer.setLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:META-INF/props/dev/*.properties"));
return propertySourcesPlaceholderConfigurer;
}
...
}
To sum up, my problem is as follows:
I am therefore looking for a new strategy to manage the differences between the different environments.
Can anyone please help?
There are many ways to do this but I think you are in the right path. Overriding of properties files gets done through BeanFactoryPostProcessors, and there's two implementations that can help you in this case so you don't have to do it from scratch:
PropertySourcesPlaceholderConfigurer & PropertyOverrideConfigurer.
This is an example using PropertySourcesPlaceholderConfigurer:
<bean id="someProperties" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer" abstract="true" >
<property name="locations">
<list>
<value>classpath:database.properties</value>
<value>classpath:email.properties</value>
</list>
</property>
<property name="ignoreUnresolvablePlaceholders" value="false"/>
</bean>
<bean id="devProperties" parent="someProperties" >
<property name="properties" >
<props >
<prop key="database.username">Database Username used for Development Environment </prop>
</props>
</property>
<property name="localOverride" value="true" />
</bean>
<bean id="testProperties" parent="someProperties" >
<property name="properties" >
<props >
<prop key="database.username">Database Username used for Testing Environment </prop>
</props>
</property>
<property name="localOverride" value="true" />
</bean>
In that example you load the default properties into a bean that will be used as a template for other beans, and in the specific bean, say TestEnvironmentProperties Bean or DevEnvironmentProperties Bean you override only the specific properties you want to override from the default properties files. The example only shows how to override specific properties without the need to create another properties file, from there you can decide how to choose which bean to create with a factory, a simple facade class or a profiles system, anything that you like and matches your architecture.
Also if you think this option is too verbose you can use the property-placeholder element.
I recommend you this book:
Getting started with Spring Framework, Second Edition
it has just the examples you need in its 5th chapter. I didn't write it or anything, I just bought it some time ago and I loved it after going through so many bad spring books.