Can @PropertySources be chosen by Spring profile?

I have a Spring 3.1 @Configuration that needs a property foo to build a bean. The property is defined in defaults.properties but may be overridden by the property in overrides.properties if the application has an active override Spring profile.

Without the override, the code would look like this, and work...

@Configuration
@PropertySource("classpath:defaults.properties")
public class MyConfiguration {

    @Autowired
    private Environment environment;

    @Bean
    public Bean bean() {
        ...
        // this.environment.getRequiredProperty("foo");
        ...
    }
}

I would like a @PropertySource for classpath:overrides.properties contingent on @Profile("overrides"). Does anyone have any ideas on how this could be achieved? Some options I've considered are a duplicate @Configuration, but that would violate DRY, or programmatic manipulation of the ConfigurableEnvironment, but I'm not sure where the environment.getPropertySources.addFirst() call would go.

Placing the following in an XML configuration works if I inject the property directly with @Value, but not when I use Environment and the getRequiredProperty() method.

<context:property-placeholder ignore-unresolvable="true" location="classpath:defaults.properties"/>

<beans profile="overrides">
    <context:property-placeholder ignore-unresolvable="true" order="0"
                                  location="classpath:overrides.properties"/>
</beans>

Update

If you're trying to do this now, check out Spring Boot's YAML support, particularly the 'Using YAML instead of Properties' section. The profile support there would make this question moot, but there isn't @PropertySource support yet.


Add the overriding @PropertySource in a static inner class. Unfortunately, you must specify all property sources together which means creating a "default" profile as the alternative to "override".

@Configuration
public class MyConfiguration
{
    @Configuration
    @Profile("default")
    @PropertySource("classpath:defaults.properties")
    static class Defaults
    { }

    @Configuration
    @Profile("override")
    @PropertySource({"classpath:defaults.properties", "classpath:overrides.properties"})
    static class Overrides
    {
        // nothing needed here if you are only overriding property values
    }

    @Autowired
    private Environment environment;

    @Bean
    public Bean bean() {
        ...
        // this.environment.getRequiredProperty("foo");
        ...
    }
}

I suggest, defining two files, where the second is optional with the profile as suffix:

@Configuration
@PropertySources({
        @PropertySource("classpath:/myconfig.properties"),
        @PropertySource(value = "classpath:/myconfig-${spring.profiles.active}.properties", ignoreResourceNotFound = true)
})
public class MyConfigurationFile {

    @Value("${my.prop1}")
    private String prop1;

    @Value("${my.prop2}")
    private String prop2;

}

You can do:

  <context:property-placeholder location="classpath:${spring.profiles.active}.properties" />

Edit: if you need something more advanced, you can register your PropertySources on application startup.

web.xml

  <context-param>
    <param-name>contextInitializerClasses</param-name>
    <param-value>com.xxx.core.spring.properties.PropertySourcesApplicationContextInitializer</param-value>
  </context-param>

file you create:

public class PropertySourcesApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {

  private static final Logger LOGGER = LoggerFactory.getLogger(PropertySourcesApplicationContextInitializer.class);

  @Override
  public void initialize(ConfigurableApplicationContext applicationContext) {
    LOGGER.info("Adding some additional property sources");
    String[] profiles = applicationContext.getEnvironment().getActiveProfiles()
    // ... Add property sources according to selected spring profile 
    // (note there already are some property sources registered, system properties etc)
    applicationContext.getEnvironment().getPropertySources().addLast(myPropertySource);
  }

}

Once you've done it you just need to add in your context:

<context:property-placeholder/>

I can't really answer to your question about multiple profiles but I guess you activate them on such an initializer, and you could register the appropriate PropertySource items during profile activations.