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_.

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.

This SO answer shows a way to do it by repeated binding. It works, but it confusing.

It'd be nice to be able to merge BindResults. Some ideas off the top of my head: 1. binder.bind accepts multiple prefixes, not just one, latter progressively overwriting the former. 2. bindResult.mergeWith(anotherBindResult), latter overwriting the former.

Comment From: wilkinsona

The answer on Stack Overflow can be simplified a little bit as you're binding to an existing instance:

FooBar fooBar = new FooBar();
binder.bind("x", Bindable.ofInstance(fooBar));
binder.bind("y", Bindable.ofInstance(fooBar));

It's obviously subjective, but that reads quite nicely to my eyes.

I think merging results would look something like this:

FooBar fooBar = new FooBar();
BindResult<FooBar> x = binder.bind("x", Bindable.ofInstance(fooBar));
BindResult<FooBar> y = binder.bind("y", Bindable.ofInstance(fooBar));
fooBar = x.mergeWith(y).get();

That's slightly more verbose and I don't find it any more readable.

Binding multiple prefixes would look something like this:

FooBar fooBar = new FooBar();
binder.bind(Arrays.asList("x", "y"), Bindable.ofInstance(fooBar));

That's more concise than binding twice, but I find it less readable. It's not as clear to me how the two prefixes will be handled.

Let's see what the rest of the team thinks.

Comment From: wilkinsona

One benefit of the multiple prefixes approach is that it would support constructor binding to an immutable type.

Comment From: asarkar

It's not as clear to me how the two prefixes will be handled

IMO, the inherent ordering in a list is a good indication that the prefixes are handled as shown.

Comment From: asarkar

One benefit of the multiple prefixes approach is that it would support constructor binding to an immutable type.

It can also be done with mergeWith?:

Bindable bindable = Bindable.of(FooBar.class);
binder.bind("x", bindable)
    .mergeWith(binder.bind("y", bindable))
    .orElse(new FooBar());

Comment From: philwebb

Not so easily unfortunately. The org.springframework.boot.context.properties.bind.ValueObjectBinder class creates objects during the actual bind operation. The BindResult object doesn't actually know how to create new instances. Things get even more complex when you consider nested object.

The Binder code is quite complex and I suspect that supporting multiple prefixes will be significantly easier than adding BindResult.mergeWith.

Comment From: philwebb

One other thing we'll need to consider is merging rules for Lists, Maps and Arrays. We should probably keep the same logic as the current Binder where Lists and Arrays are only taken from the current property source.

Comment From: philwebb

Spring Cloud would also like this feature.

Comment From: philwebb

See also #7986

Comment From: mbhave

We discussed this with the Spring Cloud team and they were able to use a different approach to support their use-case.

We think it's best not to add multiple prefix support to the binder at this time, given how complex the code is and that there is way to achieve the fallback, if necessary, by calling bind multiple times.