brabster
brabster

Reputation: 43600

Overriding Spring @PropertySource by @Import

I have a property test=default in class DefaultConfig, and I'm making them available using @PropertySource annotation.

@Configuration
@PropertySource("classpath:default.properties")
public class DefaultConfig {}

I then want to be able to override to test=override, which is in a different properties file in class OverrideConfig, so I again use @PropertySource.

@Configuration
@Import(DefaultConfig.class)
@PropertySource("classpath:override.properties")
public class OverrideConfig {}

I configure a test to prove that it works.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes={OverrideConfig.class})
public class TestPropertyOverride {

    @Autowired
    private Environment env;

    @Test
    public void propertyIsOverridden() {
        assertEquals("override", env.getProperty("test"));
    }

}

Except of course it does not.

org.junit.ComparisonFailure: expected:<[override]> but was:<[default]>

Maxing out debug, I can see what's happening:

StandardEnvironment:107 - Adding [class path resource [default.properties]] PropertySource with lowest search precedence
StandardEnvironment:107 - Adding [class path resource [override.properties]] PropertySource with lowest search precedence

It seems backwards. Am I making a simple mistake or misthinking this, or would you expect the properties defined by an @PropertySource in an @Import-ed configuration class to be overridden by properties defined in am @PropertySource in the @Import-ing class?

Upvotes: 13

Views: 14919

Answers (5)

kuporific
kuporific

Reputation: 10322

Here is a solution by Helder Sousa, written as a comment of the JIRA issue created by the OP:

The behaviour available in spring xml (one xml importing another xml) is achievable using nested configurations:

@Configuration
@PropertySource("classpath:default.properties")
public class DefaultConfig {}
@Configuration
@PropertySource("classpath:override.properties")
public class OverrideConfig {

    @Configuration
    @Import(DefaultConfig.class)
    static class InnerConfiguration {}

}

With this setup, the properties will be gathered in the proper order.

Upvotes: 7

Michael
Michael

Reputation: 3326

Today with Spring 4 you can use this:

@TestPropertySource(value="classpath:/config/test.properties")

And this can be used to use and eventually override properties for the junit test:

@RunWith(SpringJUnit4ClassRunner.class)
@TestPropertySource(value="classpath:/config/test.properties")

Upvotes: 2

membersound
membersound

Reputation: 86925

I had a similar problem and succeeded in just declaring also the default property in my custom configuration:

@Configuration
@Import(DefaultConfig.class)
@PropertySource({"classpath:default.properties", "classpath:override.properties"})
public class OverrideConfig {}

Upvotes: 1

Katherine
Katherine

Reputation: 68

You could enforce the loading order of your properties like this:

@Configuration
@PropertySource(value={"classpath:default.properties","classpath:override.properties"})
public class OverrideConfig {
...
}

Upvotes: 1

Robin
Robin

Reputation: 3396

I'm currently struggling with a similar case in Spring 3.1 but I'm using a different approach to override properties, because @PropertySource does not support optional property files:

@Configuration
@PropertySource("classpath:default.properties")
public class BaseConfig {

  @Inject
  private ApplicationContext context;

  @PostConstruct
  public void init() throws IOException {
    Resource runtimeProps = context.getResource("classpath:override.properties");
    if (runtimeProps.exists()) {
      MutablePropertySources sources = ((ConfigurableApplicationContext) context).getEnvironment().getPropertySources();
      sources.addFirst(new ResourcePropertySource(runtimeProps));
    }
  }
...

It seems that @Import does not cause any specific order of @Configuration instantiation whatsoever besides the order dictated by normal bean dependencies. A way to force such an order is to inject the base @Configuration instance itself as a dependency. Could you try:

@Configuration
@Import(DefaultConfig.class)
@PropertySource("classpath:override.properties")
public class OverrideConfig {

  @Inject
  private DefaultConfig defaultConfig;

  ...
}

Does this help? And maybe the new ContextHierarchy annotation could be of help here also but I didn't try this one out so far.

Upvotes: 1

Related Questions