Persistent @ConfigurationProperties with EnvironmentPostProcessor in Spring Boot

Configuration properties is a great feature in Spring Boot. In a monolithic long-time running application, we often want to change the application settings. Those changes must be persisted to survive the application restart.

We can use environment post-processing for that. This post describes how.

Color King (cut) by František Kupka

Let’s start slowly; consider a simple class with configuration properties:

@ConfigurationProperties("hello")
@Getter
@Setter
class HelloConfigurationProperties {

The property of greeting is used to print a greeting message from a REST controller:

@RestController
@RequestMapping("/hello")
@AllArgsConstructor
class HelloController {

After building and starting the application, we can see the default greeting to be printed:

$ curl http://localhost/hello
> Hello!

The message can be changed by setting an environment variable:

HELLO_GREETING=Hi!

As our application can’t be restarted easily (there are such cases!), we introduce a new endpoint to update the greeting message:

// ...
class HelloController {
// ...

Now it’s time to think about persistence. We introduce a class persisting properties into a database:

@RequiredArgsConstructor
public class Properties {

And use it in the controller:

// ...
class HelloController {

Now we have the changes in the database. The last step is to fill the configuration properties at the startup. It’s where the environment post-processor comes to shine:

class ConfigurationPropertiesFromDatabasePostProcessor 
implements EnvironmentPostProcessor, Ordered {
@Override
public void postProcessEnvironment(
ConfigurableEnvironment env, SpringApplication app) {
try {
DataSource dataSource = DataSourceBuilder
.create()
.url(env.getProperty("spring.datasource.url"))
.username(env.getProperty("spring.datasource.username"))
.password(env.getProperty("spring.datasource.password"))
.driverClassName(env.getProperty("spring.datasource.driver-class-name"))
.build();

Probably the biggest gotcha is the need to create a new data-source. As the environment post-processor runs before application context is refreshed, there are no ready-to-use beans so far. We have to create everything we need from scratch.

Properties from the database are loaded and put as a new property-source with the lowest priority — this means, we can still overwrite them using environment variables, system properties etc. If this behavior is not desired, we can change the order by using addFirst, addBefore or addAfter.

We have to add the post-processor entry into META-INF/spring.factories:

org.springframework.boot.env.EnvironmentPostProcessor=\
com.ttulka.ConfigurationPropertiesFromDatabasePostProcessor

That’s it! Our configuration changes now survive a restart of the application.

You can find a working example on my GitHub.

The idea is also implemented in this small Spring Boot tool:

Software developer and occasional blogger: https://blog.ttulka.com