Options for dynamic properties in Spring Boot

Viewed 1633

I have an application with some externalized configuration in the form of properties. I would like the application to react to a change of such properties without a restart or full context refresh.

I am not clear what my options are.

Artificial example: the application implements a service that receives requests and decides whether to queue or reject them. The maximum size of the queue is a property.

final int queueMaxSize = queueProperties.getMaxSize();
if (queue.size() >= queueMaxSize) { //reject }

Where QueueProperties is a class annotated with @ConfigurationProperties.

@ConfigurationProperties(prefix = "myapp.limits.queue")
@Getter
@Setter
public class QueueProperties {
  public int maxSize = 10;
}

This works as far as allowing me to control behavior via system properties, profiles, etc. However, I would like to be able to change this value without releasing/deploying/restarting/refreshing the application.

My application already uses Archaius. If I push a new value for this property using our internal infrastructure, i can see the application Spring Environment does receive the new value. (e.g., /admin/env reflects the value and changes dynamically).

The part I'm not clear on is: how to make my service react to the change of value in the environment?

I found two ways, but they seem hairy, I wonder if there are better options. I expected this to be a common problem with a first class solution in the Spring ecosystem.

Hacky solution #1:

@Bean
@Scope("prototype")
@ConfigurationProperties(prefix = "myapp.limits.queue")
QueueProperties queueProperties() {
    return new QueueProperties();
}

And inject this into the service using the properties as Provider<QueueProperties> and use it as queuePropertiesProvider.get().getMaxSize().

This works but has a few side-effects I'm not a fan of:

  • ConfigurationProperties annotation moved from the class to the bean definition
  • A new QueueProperties object is created and bound to values for every request coming in
  • Provider might throw on get()
  • Invalid values are not detected until the first request comes in

Hacky solution #2:

Don't annotate my properties class with ConfigurationProperties, inject the Spring environment at construction time. Implement the getters as such:

int getMaxSize() {
   return environment.getProperty("myapp.limits.queue", 10);
}

This also works ok in terms of behavior. However - This is not annotated as property (unlike the rest of the properties classes in this large project, makes it harder to find) - This class does not show up in /admin/configprops

Hacky solution #3:

Schedule a recurring task that uses Environment to update my singleton QueueProperties bean.

Any further ideas/suggestions/pointers? Is there a canonical/recommended way to do this that does not have the shortcoming of my solutions above?

0 Answers
Related