Allow recursive list binding when iterable source

Further refine recursive binding rules so that Lists are supported when
the underlying source is iterable.

Close gh-10702
pull/10921/merge
Phillip Webb 7 years ago
parent 5fb9162875
commit 0fbb4989da

@ -19,6 +19,7 @@ package org.springframework.boot.context.properties.bind;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.springframework.boot.context.properties.source.ConfigurationPropertyName; import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
/** /**
* Internal strategy used by {@link Binder} to bind aggregates (Maps, Lists, Arrays). * Internal strategy used by {@link Binder} to bind aggregates (Maps, Lists, Arrays).
@ -31,16 +32,17 @@ abstract class AggregateBinder<T> {
private final BindContext context; private final BindContext context;
private final boolean allowRecursiveBinding; AggregateBinder(BindContext context) {
AggregateBinder(BindContext context, boolean allowRecursiveBinding) {
this.context = context; this.context = context;
this.allowRecursiveBinding = allowRecursiveBinding;
} }
boolean isAllowRecursiveBinding() { /**
return this.allowRecursiveBinding; * Determine if recursive binding is supported.
} * @param source the configuration property source or {@code null} for all sources.
* @return if recursive binding is supported
*/
protected abstract boolean isAllowRecursiveBinding(
ConfigurationPropertySource source);
/** /**
* Perform binding for the aggregate. * Perform binding for the aggregate.

@ -266,8 +266,10 @@ public class Binder {
private <T> Object bindAggregate(ConfigurationPropertyName name, Bindable<T> target, private <T> Object bindAggregate(ConfigurationPropertyName name, Bindable<T> target,
BindHandler handler, Context context, AggregateBinder<?> aggregateBinder) { BindHandler handler, Context context, AggregateBinder<?> aggregateBinder) {
AggregateElementBinder elementBinder = (itemName, itemTarget, source) -> { AggregateElementBinder elementBinder = (itemName, itemTarget, source) -> {
boolean allowRecursiveBinding = aggregateBinder
.isAllowRecursiveBinding(source);
Supplier<?> supplier = () -> bind(itemName, itemTarget, handler, context, Supplier<?> supplier = () -> bind(itemName, itemTarget, handler, context,
aggregateBinder.isAllowRecursiveBinding()); allowRecursiveBinding);
return context.withSource(source, supplier); return context.withSource(source, supplier);
}; };
return context.withIncreasedDepth( return context.withIncreasedDepth(

@ -45,7 +45,12 @@ abstract class IndexedElementsBinder<T> extends AggregateBinder<T> {
private static final String INDEX_ZERO = "[0]"; private static final String INDEX_ZERO = "[0]";
IndexedElementsBinder(BindContext context) { IndexedElementsBinder(BindContext context) {
super(context, false); super(context);
}
@Override
protected boolean isAllowRecursiveBinding(ConfigurationPropertySource source) {
return source == null || source instanceof IterableConfigurationPropertySource;
} }
protected final void bindIndexed(ConfigurationPropertyName name, Bindable<?> target, protected final void bindIndexed(ConfigurationPropertyName name, Bindable<?> target,

@ -41,7 +41,12 @@ class MapBinder extends AggregateBinder<Map<Object, Object>> {
.mapOf(String.class, String.class); .mapOf(String.class, String.class);
MapBinder(BindContext context) { MapBinder(BindContext context) {
super(context, true); super(context);
}
@Override
protected boolean isAllowRecursiveBinding(ConfigurationPropertySource source) {
return true;
} }
@Override @Override

@ -351,6 +351,7 @@ public class CollectionBinderTests {
assertThat(result.getItemsSet()).containsExactly("a", "b", "c"); assertThat(result.getItemsSet()).containsExactly("a", "b", "c");
} }
@Test
public void bindToBeanWithNestedCollectionShouldPopulateCollection() public void bindToBeanWithNestedCollectionShouldPopulateCollection()
throws Exception { throws Exception {
MockConfigurationPropertySource source = new MockConfigurationPropertySource(); MockConfigurationPropertySource source = new MockConfigurationPropertySource();

Loading…
Cancel
Save