Skip to content

Spring Boot merge configuration properties

I’ve a use case where I need to bind configuration properties based on two prefixes, one of which is determined at runtime. Let’s say the constant prefix is foo and the runtime prefix is bar.

Given a new instance of Java Bean class FooBar, the code should bind all environment variables FOO_, then overwrite with all environment variables BAR_.

There’s a way to dynamically bind a prefix to a class, as I had stated in this ticket (sample code shown below). However, what’s missing is the merging of the results.

var bindable = Bindable.of(FooBar.class);
var properties = ConfigurationPropertySources.get(env);
new Binder(properties)
  .bind("prefix", bindable)
  .orElse(new FooBar());


public class FooBar {
    private Duration latency = Duration.ofMillis(500L);
    // other properties
    // getters, setters

If there are no environment variables FOO_LATENCY or BAR_LATENCY, FooBar.getLatency() is 500 ms. If only one of FOO_LATENCY and BAR_LATENCY is present, FooBar.getLatency() takes its value. If both FOO_LATENCY and BAR_LATENCY are present, FooBar.getLatency() takes the value of BAR_LATENCY.

Any idea how can this be done?



Just call bind again. It only assigns values that are found in the configuration properties, and last prefix bound will win, on a property-by-property basis.


class FooBar {
    private String a;
    private String b = "B";
    private String c;
    private String d;
    private String e = "E";
    // Getter, setters, and toString here

Properties (YAML)

x.a: Hello
x.b: World
z.a: Goodbye
z.c: Test


Binder binder = Binder.get(env);
FooBar fooBar = new FooBar();
fooBar = binder.bind("x", Bindable.ofInstance(fooBar)).orElse(fooBar);
fooBar = binder.bind("y", Bindable.ofInstance(fooBar)).orElse(fooBar);
fooBar = binder.bind("z", Bindable.ofInstance(fooBar)).orElse(fooBar);


FooBar[a=null, b=B, c=null, d=null, e=E]
FooBar[a=Hello, b=World, c=null, d=null, e=E]
FooBar[a=Hello, b=World, c=null, d=null, e=E]
FooBar[a=Goodbye, b=World, c=Test, d=null, e=E]

As you can see, the third binding overrides the values from the first, but only for properties that are actually configured, which is why the second binding does nothing.

I also simplified the logic to skip the use of ConfigurationPropertySources.get().