Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to post process Spring boot properties?

I have defined some external properties in my main class as follows:

@PropertySources({
        @PropertySource(value = "classpath:application.properties", ignoreResourceNotFound = true),
        @PropertySource(value = "file:/opt/app/conf/database.properties", ignoreResourceNotFound = true),
        @PropertySource(value = "file:/opt/app/conf/overrides.properties", ignoreResourceNotFound = true)
})

For some of the properties I would like to do some post processing, for example encrypting or enrichment before they are actually used by any beans. One such property is spring.datasource.password

I have done the following:

  1. Write an ApplicationContextInitializer<ConfigurableApplicationContext> and tried to process those properties in the initialize() method
public class MyApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        ConfigurableEnvironment environment = applicationContext.getEnvironment();
        String value = environment.getProperty("spring.datasource.password");
        System.out.println("The value in initializer is " + value);
        System.out.println("The database url in initializer is " + environment.getProperty("spring.datasource.url"));
        System.out.println("The database username in initializer is " + environment.getProperty("spring.datasource.username"));
    // .. other code
    }
}

and included the above class in the META-INF/spring.factories as follows:

org.springframework.context.ApplicationContextInitializer=com.myapp.MyApplicationContextInitializer

I am seeing null values in all of the above properties that are printed though both database.properties and overrides.properties are present. They are the very first statements to be printed (even before the banner)

  1. Another approach I tried is org.springframework.boot.env.EnvironmentPostProcessor and adding in META-INF/spring.factories as
org.springframework.boot.env.EnvironmentPostProcessor=com.myapp.PropertiesProcessor

But still, I get the same null value.

Interestingly, when I pass in the -Dspring.config.location="file:/opt/app/conf/database.properties, file:/opt/app/conf/overrides.properties" system property before executing the war, it works i.e. the values are printed.

But I do not want to manually pass in the system property at runtime. Is there a way to do it? What is wrong with my code or approach. Are there any other ways of doing the post-processing before actually creating the beans?

like image 493
JavaTechnical Avatar asked Jun 22 '26 02:06

JavaTechnical


2 Answers

I solved similar problem by adding property source with highest precedence.

  1. Add META-INF/spring.factories with content:
org.springframework.boot.env.EnvironmentPostProcessor=
  com.example.SettingsPropertiesAddingPostProcessor

  1. SettingsPropertiesAddingPostProcessor implementation:
public class SettingsPropertiesAddingPostProcessor implements EnvironmentPostProcessor {
    private static final String SETTINGS_CONFIG_PATH = "/tmp/settings.properties";

    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {

        File settingsFile = new File(SETTINGS_CONFIG_PATH);
        if (!settingsFile.exists()) {
            log.debug("Config file not found, skipping adding custom property source");
            return;
        }

        log.debug("Config file found, adding custom property source");
        Properties props = loadProperties(settingsFile);
        MutablePropertySources propertySources = environment.getPropertySources();
        propertySources.addFirst(new PropertiesPropertySource("settings-source", props));

    }

    private Properties loadProperties(File f) {
        FileSystemResource resource = new FileSystemResource(f);
        try {
            return PropertiesLoaderUtils.loadProperties(resource);
        } catch (IOException ex) {
            throw new IllegalStateException("Failed to load local settings from " + f.getAbsolutePath(), ex);
        }
    }
}

That should be all.

like image 157
rgrebski Avatar answered Jun 24 '26 16:06

rgrebski


Following the advice of @M. Deinum, regarding using "spring.config.additional-location", I have made a workaround as follows:

@SpringBootApplication
@EnableSwagger2
public class MyApp extends SpringBootServletInitializer {

    public static void main(String[] args) {
        System.setProperty("spring.config.additional-location", "file:/opt/app/conf/database.properties, file:/opt/app/conf/overrides.properties");
        SpringApplication springApplication = new SpringApplication(MyApp.class);
        springApplication.run(args);
    }

    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        System.out.println("Setting properties onStartup");
        System.setProperty("spring.config.additional-location", "file:/opt/app/conf/database.properties, file:/opt/app/conf/overrides.properties");
        super.onStartup(servletContext);
    }
}

I have called the System.setProperty() in the onStartup() method of the SpringBootServletInitializer by overriding it as above and then invoked the super class' onStartup()

The latter part i.e. setting system property in onStartup method helps when the application is deployed in a web container like Tomcat.

Note: We can also append the properties to spring.config.additional-location instead of set so that other additional locations can also be added during runtime.

like image 30
JavaTechnical Avatar answered Jun 24 '26 16:06

JavaTechnical



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!