piotrek
piotrek

Reputation: 14520

Plug into the hierarchy of Spring Boot property sources

I know the default order of spring's properties sources: http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html how can i add my own property source with a specific precedence?

@PropertySource is not enough as it adds new properties with very low priority

Upvotes: 2

Views: 2945

Answers (1)

Miloš Milivojević
Miloš Milivojević

Reputation: 5369

There are plenty of ways to do this; I'll just quote the official documentation:

A SpringApplication has ApplicationListeners and ApplicationContextInitializers that are used to apply customizations to the context or environment. Spring Boot loads a number of such customizations for use internally from META-INF/spring.factories. There is more than one way to register additional ones:

  • Programmatically per application by calling the addListeners and addInitializers methods on SpringApplication before you run it.
  • Declaratively per application by setting context.initializer.classes or context.listener.classes.
  • Declaratively for all applications by adding a META-INF/spring.factories and packaging a jar file that the applications all use as a library.

The SpringApplication sends some special ApplicationEvents to the listeners (even some before the context is created), and then registers the listeners for events published by the ApplicationContext as well. See Section 23.4, “Application events and listeners” in the ‘Spring Boot features’ section for a complete list.

It is also possible to customize the Environment before the application context is refreshed using EnvironmentPostProcessor. Each implementation should be registered in META-INF/spring.factories:

org.springframework.boot.env.EnvironmentPostProcessor=com.example.YourEnvironmentPostProcessor

My way was always to add an ApplicationEnvironmentPreparedEvent listener:

public class IntegrationTestBootstrapApplicationListener implements
    ApplicationListener<ApplicationEnvironmentPreparedEvent>, Ordered {

    public static final int DEFAULT_ORDER = Ordered.HIGHEST_PRECEDENCE + 4;
    public static final String PROPERTY_SOURCE_NAME = "integrationTestProps";

    private int order = DEFAULT_ORDER;

    public void setOrder(int order) {
        this.order = order;
    }

    @Override
    public int getOrder() {
        return this.order;
    }

    @Override
    public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
        ConfigurableEnvironment environment = event.getEnvironment();

        if (!environment.getPropertySources().contains(PROPERTY_SOURCE_NAME)) {
            Map<String, Object> properties = ...; // generate the values

            // use whatever precedence you want - first, last, before, after
            environment.getPropertySources().addLast(
              new MapPropertySource(PROPERTY_SOURCE_NAME, properties));
        }
    }

}

But you can just as easily use an initializer:

public class IntegrationTestBootstrapApplicationListener implements
    ApplicationContextInitializer<ConfigurableApplicationContext> {

    private static final String PROPERTY_SOURCE_NAME = "integrationTestProps";

    @Override
    public void initialize(final ConfigurableApplicationContext applicationContext) {
        ConfigurableEnvironment environment = applicationContext.getEnvironment();
        Map<String, Object> properties = ...; // generate the values

        // use whatever precedence you want - first, last, before, after
        environment.getPropertySources().addLast(
            new MapPropertySource(PROPERTY_SOURCE_NAME, properties));
    }
}

Upvotes: 3

Related Questions