diff --git a/spring-context/src/main/java/org/springframework/validation/DataBinder.java b/spring-context/src/main/java/org/springframework/validation/DataBinder.java index bd804bfe6479..37f93d182249 100644 --- a/spring-context/src/main/java/org/springframework/validation/DataBinder.java +++ b/spring-context/src/main/java/org/springframework/validation/DataBinder.java @@ -341,7 +341,7 @@ public void initDirectFieldAccess() { */ protected AbstractPropertyBindingResult createDirectFieldBindingResult() { DirectFieldBindingResult result = new DirectFieldBindingResult(getTarget(), - getObjectName(), isAutoGrowNestedPaths()); + getObjectName(), isAutoGrowNestedPaths(), getAutoGrowCollectionLimit()); if (this.conversionService != null) { result.initConversion(this.conversionService); diff --git a/spring-context/src/main/java/org/springframework/validation/DirectFieldBindingResult.java b/spring-context/src/main/java/org/springframework/validation/DirectFieldBindingResult.java index 9e57053ac293..68f7537354c3 100644 --- a/spring-context/src/main/java/org/springframework/validation/DirectFieldBindingResult.java +++ b/spring-context/src/main/java/org/springframework/validation/DirectFieldBindingResult.java @@ -19,6 +19,7 @@ import org.jspecify.annotations.Nullable; import org.springframework.beans.ConfigurablePropertyAccessor; +import org.springframework.beans.DirectFieldAccessor; import org.springframework.beans.PropertyAccessorFactory; /** @@ -41,6 +42,8 @@ public class DirectFieldBindingResult extends AbstractPropertyBindingResult { private final boolean autoGrowNestedPaths; + private final int autoGrowCollectionLimit; + private transient @Nullable ConfigurablePropertyAccessor directFieldAccessor; @@ -60,9 +63,24 @@ public DirectFieldBindingResult(@Nullable Object target, String objectName) { * @param autoGrowNestedPaths whether to "auto-grow" a nested path that contains a null value */ public DirectFieldBindingResult(@Nullable Object target, String objectName, boolean autoGrowNestedPaths) { + this(target, objectName, autoGrowNestedPaths, Integer.MAX_VALUE); + } + + /** + * Create a new {@code DirectFieldBindingResult} for the given target. + * @param target the target object to bind onto + * @param objectName the name of the target object + * @param autoGrowNestedPaths whether to "auto-grow" a nested path that contains a null value + * @param autoGrowCollectionLimit the limit for array and collection auto-growing + * @since 7.1 + */ + public DirectFieldBindingResult(@Nullable Object target, String objectName, + boolean autoGrowNestedPaths, int autoGrowCollectionLimit) { + super(objectName); this.target = target; this.autoGrowNestedPaths = autoGrowNestedPaths; + this.autoGrowCollectionLimit = autoGrowCollectionLimit; } @@ -82,6 +100,7 @@ public final ConfigurablePropertyAccessor getPropertyAccessor() { this.directFieldAccessor = createDirectFieldAccessor(); this.directFieldAccessor.setExtractOldValueForEditor(true); this.directFieldAccessor.setAutoGrowNestedPaths(this.autoGrowNestedPaths); + ((DirectFieldAccessor) this.directFieldAccessor).setAutoGrowCollectionLimit(this.autoGrowCollectionLimit); } return this.directFieldAccessor; } diff --git a/spring-context/src/test/java/org/springframework/validation/DataBinderFieldAccessTests.java b/spring-context/src/test/java/org/springframework/validation/DataBinderFieldAccessTests.java index cc1e22ad51c9..c2c925b02e3a 100644 --- a/spring-context/src/test/java/org/springframework/validation/DataBinderFieldAccessTests.java +++ b/spring-context/src/test/java/org/springframework/validation/DataBinderFieldAccessTests.java @@ -17,10 +17,12 @@ package org.springframework.validation; import java.beans.PropertyEditorSupport; +import java.util.List; import java.util.Map; import org.junit.jupiter.api.Test; +import org.springframework.beans.InvalidPropertyException; import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.NotWritablePropertyException; import org.springframework.beans.NullValueInNestedPathException; @@ -134,6 +136,25 @@ void nestedBindingWithDisabledAutoGrow() { binder.bind(pvs)); } + @Test + void directFieldAccessHonorsDefaultAutoGrowCollectionLimit() { + FieldAccessForm target = new FieldAccessForm(); + DataBinder binder = new DataBinder(target); + binder.initDirectFieldAccess(); + + MutablePropertyValues pvs = new MutablePropertyValues(); + pvs.add("items[255].name", "value"); + binder.bind(pvs); + + assertThat(target.items).hasSize(256); + assertThat(target.items.get(255).name).isEqualTo("value"); + + MutablePropertyValues outOfBounds = new MutablePropertyValues(); + outOfBounds.add("items[256].name", "too-far"); + assertThatExceptionOfType(InvalidPropertyException.class).isThrownBy(() -> + binder.bind(outOfBounds)); + } + @Test void bindingWithErrorsAndCustomEditors() { FieldAccessBean rod = new FieldAccessBean(); @@ -176,4 +197,16 @@ public String getAsText() { assertThat(tb.getSpouse()).isNotNull(); }); } + + + static class FieldAccessForm { + + public List items; + } + + + static class FieldAccessItem { + + public String name; + } }