Different property variable for Local and prod Environment (Spring)

You can load properties based on the current spring profile or profiles. To set a spring profile I mostly set a system property named spring.profiles.active to the desired value e.g. development or production.

The concept is pretty simple. Read the currently active profile from the system property. Build the filename and load the properties file using a PropertySourcesPlaceholderConfigurer. Using the PropertySourcesPlaceholderConfigurer will make it easier the access those properties through @Value annotations. Note that this examples assumes one profile is active. It may need some extra care when multiple profiles are active.

Java based configuration

@Configuration
public class MyApplicationConfiguration {

    @Bean
    public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
        String activeProfile = System.getProperty("spring.profiles.active", "production");
        String propertiesFilename = "app-" + activeProfile + ".properties";

        PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
        configurer.setLocation(new ClassPathResource(propertiesFilename));

        return configurer;
    }
}

You could also import multiple configuration classes annotated with @Profile. Spring would select which configuration to use based on the currently active profiles. Every class could add it's own version of PropertySourcesPlaceholderConfigurer to the application context.

@Configuration
@Import({Development.class, Production.class})
public class MyApplicationConfiguration {}

@Configuration
@Profile("development")
public class Development {}

@Configuration
@Profile // The default
public class Production {}

As Emerson Farrugia stated in his comment the @Profile per class approach is a bit drastic for selecting a PropertySourcesPlaceholderConfigurer. Annotating a @Bean declaration would be much simpler.

@Configuration
public class MyApplicationConfiguration {

    @Bean
    @Profile("development")
    public static PropertySourcesPlaceholderConfigurer developmentPropertyPlaceholderConfigurer() {
        // instantiate and return configurer...
    }

    @Bean
    @Profile // The default
    public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
        // instantiate and return configurer...
    }
}

I've been banging my head half a day on this, and the solution I've ended up with is a combination of the other answers here.

Bart's solution had for me the drawback of using a dynamic prop file, so that my IDE "forgot" everything about it and couldn't suggest me which properties were used and where. Geoand solution had for me the drawback that the config file "should be in place somewhere". I can see why this is convenient in certain scenarios, but I tend to prefer one single artifact deployable in any environment, so I don't have to "park" files in strategic places for the app to pick up.

The solution for me therefore is:

- config.properties (default properties, usually local development env)
- config.staging.properties (overrides depending on the env)
- config.prod.properties (overrides depending on the env)

<context:property-placeholder ignore-resource-not-found="true"
                              location="config.properties,
                                        config-${profile}.properties"/>

The magic is the ignore-resource-not-found="true" that allows me to fire my local development env without setting any special profile variable. The missing file will just be ignored.

Instead, I set that variable in my various deployment environment, e.g. -Dprofile=staging, and then the file will be found and the overrides applied.


One solution that does not involve Spring Profiles at all is to use something like the following:

<context:property-placeholder location="classpath*:config.properties,file:/path/config/config.properties"
                              ignore-unresolvable="false" ignore-resource-not-found="true"/>

What this does is tell Spring to look for properties using two files, the file in your jar/war, and one that can be anywhere in the file system. The ignore-resource-not-found means that if one of the files is not found, Spring won't complain.

Using this setup the second file can be controlled by DevOps people and can contain anything of their choosing, thus overriding any properties in your classpath properties file file.

UPDATE:

You could do the same with Java Config using the following bean in your configuration class

@Bean
public static PropertySourcesPlaceholderConfigurer properties() {
    final PropertySourcesPlaceholderConfigurer pspc = new PropertySourcesPlaceholderConfigurer();

    Resource[] resources = new Resource[ ] {
            new ClassPathResource( "config.properties" ),
            new FileSystemResource("/path/config/config.properties")
    };

    pspc.setLocations( resources );
    pspc.setIgnoreResourceNotFound(true);
    pspc.setIgnoreUnresolvablePlaceholders(false);
    return pspc;
}