Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Correctly using Spring environment profiles in order to manage PropertySourcesPlaceholderConfigurer and sets of properties files

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:

properties file screen capture

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:

  • key/value pairs are duplicated all over the 5 different folders because only a few values are different.

I am therefore looking for a new strategy to manage the differences between the different environments.

Can anyone please help?

like image 491
balteo Avatar asked May 12 '14 20:05

balteo


People also ask

How do you load properties file based on environment in spring boot?

Environment-Specific Properties File. If we need to target different environments, there's a built-in mechanism for that in Boot. We can simply define an application-environment. properties file in the src/main/resources directory, and then set a Spring profile with the same environment name.

What 2 types of file formats can use to inject properties into the spring environment object?

You can use properties files, YAML files, environment variables and command-line arguments to externalize configuration.

What is the benefit of using spring boot profiles?

Spring Profiles provides an efficient way to bundle properties according to functionality, thus freeing the Developer from the tedium of other solutions to problem solve a growing set of application properties.

Which spring boot property sets the profile for an application?

Spring profiles can also be activated via Maven profiles, by specifying the spring. profiles. active configuration property. This command will package the application for prod profile.


2 Answers

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.

like image 189
Langley Avatar answered Oct 10 '22 22:10

Langley


Pull the common properties into a separate file and specify that plus the profile specific properties as inputs for each profile. Haven't used the Java based Spring config but here's how I do it in XML. Assume you can do the same in code:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">

    <beans profile="default">
        <bean id="applicationPropertiesPlaceholder"
            class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
            <property name="locations">
                <list>
                    <value>classpath:profiles/common.profile.properties</value>
                    <value>classpath:profiles/local.profile.properties</value>
                </list>
            </property>
        </bean>
    </beans>

    <beans profile="local">
        <bean id="applicationPropertiesPlaceholder"
            class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
            <property name="locations">
                <list>
                    <value>classpath:profiles/common.profile.properties</value>
                    <value>classpath:profiles/local.profile.properties</value>
                </list>
            </property>
        </bean>
    </beans>

    <beans profile="trial">
        <bean id="applicationPropertiesPlaceholder"
            class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
            <property name="locations">
                <list>
                    <value>classpath:profiles/common.profile.properties</value>
                    <value>classpath:profiles/trial.profile.properties</value>
                </list>
            </property>
        </bean>
    </beans>

    <beans profile="live">
        <bean id="applicationPropertiesPlaceholder"
            class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
            <property name="locations">
                <list>
                    <value>classpath:profiles/common.profile.properties</value>
                    <value>classpath:profiles/live.profile.properties</value>
                </list>
            </property>
        </bean>
    </beans>

</beans>
like image 21
Alan Hay Avatar answered Oct 10 '22 22:10

Alan Hay