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())
}
}
}
}
}