diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/AggregateBinder.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/AggregateBinder.java index 3eb97bfd1d..4dcd3be831 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/AggregateBinder.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/AggregateBinder.java @@ -19,6 +19,7 @@ package org.springframework.boot.context.properties.bind; import java.util.function.Supplier; 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). @@ -31,16 +32,17 @@ abstract class AggregateBinder { private final BindContext context; - private final boolean allowRecursiveBinding; - - AggregateBinder(BindContext context, boolean allowRecursiveBinding) { + AggregateBinder(BindContext 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. diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/Binder.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/Binder.java index 26b7c4dad6..eb6d978221 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/Binder.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/Binder.java @@ -266,8 +266,10 @@ public class Binder { private Object bindAggregate(ConfigurationPropertyName name, Bindable target, BindHandler handler, Context context, AggregateBinder aggregateBinder) { AggregateElementBinder elementBinder = (itemName, itemTarget, source) -> { + boolean allowRecursiveBinding = aggregateBinder + .isAllowRecursiveBinding(source); Supplier supplier = () -> bind(itemName, itemTarget, handler, context, - aggregateBinder.isAllowRecursiveBinding()); + allowRecursiveBinding); return context.withSource(source, supplier); }; return context.withIncreasedDepth( diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/IndexedElementsBinder.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/IndexedElementsBinder.java index 123190f71e..2710493c03 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/IndexedElementsBinder.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/IndexedElementsBinder.java @@ -45,7 +45,12 @@ abstract class IndexedElementsBinder extends AggregateBinder { private static final String INDEX_ZERO = "[0]"; 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, diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/MapBinder.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/MapBinder.java index 358d3db96e..0691274153 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/MapBinder.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/MapBinder.java @@ -41,7 +41,12 @@ class MapBinder extends AggregateBinder> { .mapOf(String.class, String.class); MapBinder(BindContext context) { - super(context, true); + super(context); + } + + @Override + protected boolean isAllowRecursiveBinding(ConfigurationPropertySource source) { + return true; } @Override diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/CollectionBinderTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/CollectionBinderTests.java index 7479a8dfaa..699ac52ab6 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/CollectionBinderTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/CollectionBinderTests.java @@ -351,6 +351,7 @@ public class CollectionBinderTests { assertThat(result.getItemsSet()).containsExactly("a", "b", "c"); } + @Test public void bindToBeanWithNestedCollectionShouldPopulateCollection() throws Exception { MockConfigurationPropertySource source = new MockConfigurationPropertySource();