Align SpEL's default max auto-grow size with Spring data binding

Prior to this commit, the SpelParserConfiguration constructors that
omit an explicit maximumAutoGrowSize left collection auto-growing
effectively unbounded, defaulting to Integer.MAX_VALUE. That default
was inconsistent with the auto-grow limit applied elsewhere in the
framework for data binding (see
DataBinder.DEFAULT_AUTO_GROW_COLLECTION_LIMIT).

To address that, this commit introduces a new
SpelParserConfiguration.DEFAULT_MAX_AUTO_GROW_SIZE constant (set to 256
to match DataBinder.DEFAULT_AUTO_GROW_COLLECTION_LIMIT) and switches
the constructors that previously hard-coded Integer.MAX_VALUE to use
this new default instead. Constructors that accept an explicit
maximumAutoGrowSize are unaffected.

In addition, SpelParserConfiguration now enforces that a user-supplied
maximumAutoGrowSize is not a negative value, consistent with the
preconditions already enforced for maximumExpressionLength,
maximumOperations, maximumBigPowerBits, and maximumNestingDepth. A
value of 0 remains supported (effectively disabling collection
auto-growing) and is now documented as such in the Javadoc.

The Spring Framework reference documentation has also been updated to
describe the new default, and tests have been added to IndexingTests to
verify the default, the ability to override it, and the new
precondition.

Closes gh-36995
This commit is contained in:
Sam Brannen
2026-08-21 14:11:04 +02:00
parent 68d438c9ff
commit fb240829b3
3 changed files with 102 additions and 9 deletions
@@ -554,6 +554,14 @@ Kotlin::
----
======
When collection auto-growing is enabled, a collection cannot automatically grow beyond
256 elements by default; however, the `maximumAutoGrowSize` value is configurable. This
default is aligned with `DataBinder.DEFAULT_AUTO_GROW_COLLECTION_LIMIT`, for consistency
with the auto-grow limit used for data binding in Spring MVC and Spring WebFlux. If you
create a `SpelExpressionParser` programmatically, you can specify a custom
`maximumAutoGrowSize` when creating the `SpelParserConfiguration` that you provide to the
`SpelExpressionParser`.
By default, a SpEL expression cannot contain more than 10,000 characters; however, the
`maxExpressionLength` is configurable. If you create a `SpelExpressionParser`
programmatically, you can specify a custom `maxExpressionLength` when creating the
@@ -36,6 +36,15 @@ import org.springframework.util.StringUtils;
*/
public class SpelParserConfiguration {
/**
* Default maximum size to which a collection or array can automatically grow: {@value}.
* <p>Aligned with the default auto-grow limit used for Spring's data binding
* support (see {@code DataBinder.DEFAULT_AUTO_GROW_COLLECTION_LIMIT}), for
* consistency between SpEL and data binding.
* @since 7.1
*/
public static final int DEFAULT_MAX_AUTO_GROW_SIZE = 256;
/**
* Default maximum length permitted for a SpEL expression: {@value}.
* @since 5.2.24
@@ -162,9 +171,10 @@ public class SpelParserConfiguration {
* @see #SPRING_EXPRESSION_COMPILER_MODE_PROPERTY_NAME
* @see #SPRING_EXPRESSION_MAX_OPERATIONS_PROPERTY_NAME
* @see #SPRING_EXPRESSION_MAX_BIG_POWER_BITS_PROPERTY_NAME
* @see #DEFAULT_MAX_AUTO_GROW_SIZE
*/
public SpelParserConfiguration() {
this(null, null, false, false, Integer.MAX_VALUE);
this(null, null);
}
/**
@@ -180,9 +190,10 @@ public class SpelParserConfiguration {
* @see #SPRING_EXPRESSION_COMPILER_MODE_PROPERTY_NAME
* @see #SPRING_EXPRESSION_MAX_OPERATIONS_PROPERTY_NAME
* @see #SPRING_EXPRESSION_MAX_BIG_POWER_BITS_PROPERTY_NAME
* @see #DEFAULT_MAX_AUTO_GROW_SIZE
*/
public SpelParserConfiguration(@Nullable SpelCompilerMode compilerMode, @Nullable ClassLoader compilerClassLoader) {
this(compilerMode, compilerClassLoader, false, false, Integer.MAX_VALUE);
this(compilerMode, compilerClassLoader, false, false, DEFAULT_MAX_AUTO_GROW_SIZE);
}
/**
@@ -196,9 +207,10 @@ public class SpelParserConfiguration {
* @see #SPRING_EXPRESSION_COMPILER_MODE_PROPERTY_NAME
* @see #SPRING_EXPRESSION_MAX_OPERATIONS_PROPERTY_NAME
* @see #SPRING_EXPRESSION_MAX_BIG_POWER_BITS_PROPERTY_NAME
* @see #DEFAULT_MAX_AUTO_GROW_SIZE
*/
public SpelParserConfiguration(boolean autoGrowNullReferences, boolean autoGrowCollections) {
this(null, null, autoGrowNullReferences, autoGrowCollections, Integer.MAX_VALUE);
this(autoGrowNullReferences, autoGrowCollections, DEFAULT_MAX_AUTO_GROW_SIZE);
}
/**
@@ -209,7 +221,9 @@ public class SpelParserConfiguration {
* global defaults per use case.
* @param autoGrowNullReferences if null references should automatically grow
* @param autoGrowCollections if collections should automatically grow
* @param maximumAutoGrowSize the maximum size to which a collection can auto grow
* @param maximumAutoGrowSize the maximum size to which a collection can auto grow;
* must not be negative, and a value of {@code 0} effectively disables growing
* a collection beyond its current size
* @see #SPRING_EXPRESSION_COMPILER_MODE_PROPERTY_NAME
* @see #SPRING_EXPRESSION_MAX_OPERATIONS_PROPERTY_NAME
* @see #SPRING_EXPRESSION_MAX_BIG_POWER_BITS_PROPERTY_NAME
@@ -230,7 +244,9 @@ public class SpelParserConfiguration {
* expression compilation; or {@code null} to use the default {@code ClassLoader}
* @param autoGrowNullReferences if null references should automatically grow
* @param autoGrowCollections if collections should automatically grow
* @param maximumAutoGrowSize the maximum size to which a collection can auto grow
* @param maximumAutoGrowSize the maximum size to which a collection can auto grow;
* must not be negative, and a value of {@code 0} effectively disables growing
* a collection beyond its current size
* @see #SPRING_EXPRESSION_COMPILER_MODE_PROPERTY_NAME
* @see #SPRING_EXPRESSION_MAX_OPERATIONS_PROPERTY_NAME
* @see #SPRING_EXPRESSION_MAX_BIG_POWER_BITS_PROPERTY_NAME
@@ -254,7 +270,9 @@ public class SpelParserConfiguration {
* expression compilation; or {@code null} to use the default {@code ClassLoader}
* @param autoGrowNullReferences if null references should automatically grow
* @param autoGrowCollections if collections should automatically grow
* @param maximumAutoGrowSize the maximum size to which a collection can auto grow
* @param maximumAutoGrowSize the maximum size to which a collection can auto grow;
* must not be negative, and a value of {@code 0} effectively disables growing
* a collection beyond its current size
* @param maximumExpressionLength the maximum length of a SpEL expression;
* must be a positive number
* @since 5.2.25
@@ -282,7 +300,9 @@ public class SpelParserConfiguration {
* expression compilation; or {@code null} to use the default {@code ClassLoader}
* @param autoGrowNullReferences if null references should automatically grow
* @param autoGrowCollections if collections should automatically grow
* @param maximumAutoGrowSize the maximum size to which a collection can auto grow
* @param maximumAutoGrowSize the maximum size to which a collection can auto grow;
* must not be negative, and a value of {@code 0} effectively disables growing
* a collection beyond its current size
* @param maximumExpressionLength the maximum length of a SpEL expression;
* must be a positive number
* @param maximumOperations the maximum number of operations permitted during
@@ -311,7 +331,9 @@ public class SpelParserConfiguration {
* expression compilation; or {@code null} to use the default {@code ClassLoader}
* @param autoGrowNullReferences if null references should automatically grow
* @param autoGrowCollections if collections should automatically grow
* @param maximumAutoGrowSize the maximum size to which a collection can auto grow
* @param maximumAutoGrowSize the maximum size to which a collection can auto grow;
* must not be negative, and a value of {@code 0} effectively disables growing
* a collection beyond its current size
* @param maximumExpressionLength the maximum length of a SpEL expression;
* must be a positive number
* @param maximumOperations the maximum number of operations permitted during
@@ -338,7 +360,9 @@ public class SpelParserConfiguration {
* expression compilation; or {@code null} to use the default {@code ClassLoader}
* @param autoGrowNullReferences if null references should automatically grow
* @param autoGrowCollections if collections should automatically grow
* @param maximumAutoGrowSize the maximum size to which a collection can auto grow
* @param maximumAutoGrowSize the maximum size to which a collection can auto grow;
* must not be negative, and a value of {@code 0} effectively disables growing
* a collection beyond its current size
* @param maximumExpressionLength the maximum length of a SpEL expression;
* must be a positive number
* @param maximumOperations the maximum number of operations permitted during
@@ -355,6 +379,7 @@ public class SpelParserConfiguration {
int maximumOperations, int maximumBigPowerBits, int maximumNestingDepth) {
Assert.notNull(compilerMode, "'compilerMode' must not be null");
Assert.isTrue(maximumAutoGrowSize >= 0, "'maximumAutoGrowSize' must not be negative");
Assert.isTrue(maximumExpressionLength > 0, "'maximumExpressionLength' must be a positive number");
Assert.isTrue(maximumOperations > 0, "'maximumOperations' must be a positive number");
Assert.isTrue(maximumBigPowerBits > 0, "'maximumBigPowerBits' must be a positive number");
@@ -402,6 +427,7 @@ public class SpelParserConfiguration {
/**
* Return the maximum size to which a collection can auto grow.
* @see #DEFAULT_MAX_AUTO_GROW_SIZE
*/
public int getMaximumAutoGrowSize() {
return this.maximumAutoGrowSize;
@@ -55,6 +55,7 @@ import org.springframework.expression.spel.testresources.Person;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.given;
@@ -66,6 +67,7 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.springframework.expression.spel.SpelMessage.EXCEPTION_DURING_INDEX_READ;
import static org.springframework.expression.spel.SpelMessage.EXCEPTION_DURING_INDEX_WRITE;
import static org.springframework.expression.spel.SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE;
import static org.springframework.expression.spel.SpelMessage.UNABLE_TO_GROW_COLLECTION;
import static org.springframework.expression.spel.SpelMessage.UNABLE_TO_GROW_COLLECTION_UNKNOWN_ELEMENT_TYPE;
@SuppressWarnings("rawtypes")
@@ -326,6 +328,63 @@ class IndexingTests {
.satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(UNABLE_TO_GROW_COLLECTION_UNKNOWN_ELEMENT_TYPE));
}
@Nested // gh-36995
class MaxAutoGrowSizeTests {
@Test
void defaultMaxAutoGrowSizeMatchesDataBinderDefault() {
SpelParserConfiguration configuration = new SpelParserConfiguration(true, true);
assertThat(configuration.getMaximumAutoGrowSize())
.isEqualTo(SpelParserConfiguration.DEFAULT_MAX_AUTO_GROW_SIZE)
.isEqualTo(256);
}
@Test
void zeroMaximumAutoGrowSizeIsAllowedAndDisablesGrowth() {
decimals = new ArrayList<>();
SpelParserConfiguration configuration = new SpelParserConfiguration(true, true, 0);
SpelExpressionParser parser = new SpelExpressionParser(configuration);
Expression indexExpression = parser.parseExpression("decimals[0]");
assertThatExceptionOfType(SpelEvaluationException.class)
.isThrownBy(() -> indexExpression.getValue(IndexingTests.this))
.satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(UNABLE_TO_GROW_COLLECTION));
}
@Test
void negativeMaximumAutoGrowSizeIsRejected() {
assertThatIllegalArgumentException()
.isThrownBy(() -> new SpelParserConfiguration(true, true, -1))
.withMessage("'maximumAutoGrowSize' must not be negative");
}
@Test
void collectionGrowsUpToDefaultMaxAutoGrowSizeButNotBeyond() {
decimals = new ArrayList<>();
SpelExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
// Growing up to the default maximum auto-grow size should succeed.
parser.parseExpression("decimals[255]").getValue(IndexingTests.this);
assertThat(decimals).hasSize(256);
// Growing beyond the default maximum auto-grow size should fail.
Expression indexExpression = parser.parseExpression("decimals[256]");
assertThatExceptionOfType(SpelEvaluationException.class)
.isThrownBy(() -> indexExpression.getValue(IndexingTests.this))
.satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(UNABLE_TO_GROW_COLLECTION));
}
@Test
void collectionCanGrowBeyondDefaultMaxAutoGrowSizeWhenConfiguredExplicitly() {
decimals = new ArrayList<>();
SpelParserConfiguration configuration = new SpelParserConfiguration(true, true, 1000);
SpelExpressionParser parser = new SpelExpressionParser(configuration);
parser.parseExpression("decimals[500]").getValue(IndexingTests.this);
assertThat(decimals).hasSize(501);
}
}
@Test
void indexIntoGenericPropertyContainingArray() {
String[] property = { "bar" };