Copy Spring Boot Property Sources into System Properties

Spring Boot is unique in that property values are accessed through Spring’s Environment abstraction. However, certain libraries require properties to be accessible through the System.getProperty(...) convention. Adding a custom factory around the @PropertySource annotation to force properties into the system context is a good solution, however, it might not be the most robust. Anything that might get bootstrapped during the Spring initialization process before the property sources are loaded from the annotation might be misconfigured based on properties that haven’t loaded yet.

To prevent this from happening, a application listener can be configured to listen for the ApplicationEnvironmentPreparedEvent then load properties into the system context before much else has occurred. This listener should be used in conjunction with the system property spring.config.location to set the location of properties to load instead of the @PropertySource annotation. For more information on external configuration, see the Spring Documentation.

The Java and Kotlin examples below demo this implementation.

Java Version

@SpringBootApplication
public class Application {
  public static void main(String[] args) {
      SpringApplication springApplication = new SpringApplication(Application.class);
      springApplication.addListener(new SystemPropertiesSetterEventListener());
      springApplication.run(args);
  }

  private static class SystemPropertiesSetterEventListener
      implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {
    @Override
    public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
      event.getEnvironment().getPropertySources().forEach(ps -> {
        if (ps instanceof MapPropertySource) {
          MapPropertySource mps = (MapPropertySource) ps;
          mps.getSource().forEach((key, value) -> System.getProperties().putIfAbsent(key, value.toString()));
        }
      });
    }
  }
}

Kotlin Version

@SpringBootApplication
class Application

fun main(args: Array<String>) {
  runApplication<Application>(args = *args, init = {
    this.addListeners(SystemPropertiesSetterEventListener())
  })
}

class SystemPropertiesSetterEventListener :
    ApplicationListener<ApplicationEnvironmentPreparedEvent> {
  override fun onApplicationEvent(event: ApplicationEnvironmentPreparedEvent) {
    event.environment.propertySources.forEach { ps ->
      if (ps is MapPropertySource) {
        ps.source.forEach {
          System.getProperties().putIfAbsent(it.key, it.value.toString())
        }
      }
    }
  }
}