Apply auto-grow limit to direct field binding

DataBinder applies its auto-grow collection limit to bean
property access, but direct field access left DirectFieldAccessor
at its default limit.

Pass DataBinder's configured limit into DirectFieldBindingResult
and apply it to the DirectFieldAccessor.

Closes gh-36861

Signed-off-by: Matthias Kurz <m.kurz@irregular.at>
This commit is contained in:
Matthias Kurz
2026-06-02 12:25:19 +02:00
committed by Juergen Hoeller
parent 85a8868bae
commit 481a5743b3
3 changed files with 53 additions and 1 deletions
@@ -341,7 +341,7 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
*/
protected AbstractPropertyBindingResult createDirectFieldBindingResult() {
DirectFieldBindingResult result = new DirectFieldBindingResult(getTarget(),
getObjectName(), isAutoGrowNestedPaths());
getObjectName(), isAutoGrowNestedPaths(), getAutoGrowCollectionLimit());
if (this.conversionService != null) {
result.initConversion(this.conversionService);
@@ -19,6 +19,7 @@ package org.springframework.validation;
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 class DirectFieldBindingResult extends AbstractPropertyBindingResult {
* @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 class DirectFieldBindingResult extends AbstractPropertyBindingResult {
this.directFieldAccessor = createDirectFieldAccessor();
this.directFieldAccessor.setExtractOldValueForEditor(true);
this.directFieldAccessor.setAutoGrowNestedPaths(this.autoGrowNestedPaths);
((DirectFieldAccessor) this.directFieldAccessor).setAutoGrowCollectionLimit(this.autoGrowCollectionLimit);
}
return this.directFieldAccessor;
}
@@ -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 @@ class DataBinderFieldAccessTests {
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 @@ class DataBinderFieldAccessTests {
assertThat(tb.getSpouse()).isNotNull();
});
}
static class FieldAccessForm {
public List<FieldAccessItem> items;
}
static class FieldAccessItem {
public String name;
}
}