From 91eb42645e26a7ef9382b4a655bcefe5c8682fee Mon Sep 17 00:00:00 2001 From: Sam Brannen <104798+sbrannen@users.noreply.github.com> Date: Sat, 22 Aug 2026 13:17:14 +0200 Subject: [PATCH] Deprecate SpelParserConfiguration constructors in favor of the builder API Since we now have an official builder API for SpelParserConfiguration (introduced in 7.0.10), this commit follows through on the plan stated in that commit's Javadoc and formally deprecates all 9 overloaded constructors in SpelParserConfiguration, thereby encouraging users to benefit from the simplicity of the builder API -- or SpelParserConfiguration.withDefaults() for the common case -- instead of having to migrate to the latest-and-greatest full constructor every time a new configuration property is introduced. The no-arg constructor points users to withDefaults(), and all other constructors -- including the canonical 9-parameter constructor -- point to the builder API. Builder.build() has been annotated with @SuppressWarnings("deprecation"), since it is the sole legitimate internal caller of the now-deprecated canonical constructor. The SpelParserConfigurationTests.LegacyConstructorTests nested class (and its sibling builderAppliesSameDefaultsAsNoArgConstructor() test method) are annotated with @SuppressWarnings("deprecation"), since they exist specifically to provide regression coverage for the deprecated constructors. IndexingTests.MaxAutoGrowSizeTests and SpelParserTests.MaxNestingDepthTests, on the other hand, were both introduced before the builder API existed and had no such need for the legacy constructors, so they have been converted to use the builder API instead, avoiding the need for any deprecation suppression there. See gh-37187 Closes gh-37190 --- .../spel/SpelParserConfiguration.java | 23 +++++++++++++++++-- .../expression/spel/IndexingTests.java | 14 +++++++---- .../spel/SpelParserConfigurationTests.java | 2 ++ .../spel/standard/SpelParserTests.java | 17 +++++--------- 4 files changed, 38 insertions(+), 18 deletions(-) diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/SpelParserConfiguration.java b/spring-expression/src/main/java/org/springframework/expression/spel/SpelParserConfiguration.java index 39b704c017f..e14e5209e48 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/SpelParserConfiguration.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/SpelParserConfiguration.java @@ -32,8 +32,8 @@ import org.springframework.util.StringUtils; * configure and create a {@code SpelParserConfiguration} instance, since the * builder only requires configuration of the properties that need to deviate from * their sensible defaults — or use {@link #withDefaults()} if none of those - * defaults need to be overridden. Note that the constructors in this class are - * planned to be deprecated in favor of the builder as of Spring Framework 7.1. + * defaults need to be overridden. Note that the constructors in this class have + * been deprecated in favor of the builder as of Spring Framework 7.1. * * @author Juergen Hoeller * @author Phillip Webb @@ -205,7 +205,9 @@ public class SpelParserConfiguration { * @see #SPRING_EXPRESSION_MAX_OPERATIONS_PROPERTY_NAME * @see #SPRING_EXPRESSION_MAX_BIG_POWER_BITS_PROPERTY_NAME * @see #DEFAULT_MAX_AUTO_GROW_SIZE + * @deprecated as of Spring Framework 7.1, in favor of {@link #withDefaults()} */ + @Deprecated(since = "7.1") public SpelParserConfiguration() { this(null, null); } @@ -223,7 +225,9 @@ public class SpelParserConfiguration { * @see #SPRING_EXPRESSION_MAX_OPERATIONS_PROPERTY_NAME * @see #SPRING_EXPRESSION_MAX_BIG_POWER_BITS_PROPERTY_NAME * @see #DEFAULT_MAX_AUTO_GROW_SIZE + * @deprecated as of Spring Framework 7.1, in favor of the {@linkplain #builder() builder API} */ + @Deprecated(since = "7.1") public SpelParserConfiguration(@Nullable SpelCompilerMode compilerMode, @Nullable ClassLoader compilerClassLoader) { this(compilerMode, compilerClassLoader, false, false, DEFAULT_MAX_AUTO_GROW_SIZE); } @@ -239,7 +243,9 @@ public class SpelParserConfiguration { * @see #SPRING_EXPRESSION_MAX_OPERATIONS_PROPERTY_NAME * @see #SPRING_EXPRESSION_MAX_BIG_POWER_BITS_PROPERTY_NAME * @see #DEFAULT_MAX_AUTO_GROW_SIZE + * @deprecated as of Spring Framework 7.1, in favor of the {@linkplain #builder() builder API} */ + @Deprecated(since = "7.1") public SpelParserConfiguration(boolean autoGrowNullReferences, boolean autoGrowCollections) { this(autoGrowNullReferences, autoGrowCollections, DEFAULT_MAX_AUTO_GROW_SIZE); } @@ -257,7 +263,9 @@ 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 + * @deprecated as of Spring Framework 7.1, in favor of the {@linkplain #builder() builder API} */ + @Deprecated(since = "7.1") public SpelParserConfiguration(boolean autoGrowNullReferences, boolean autoGrowCollections, int maximumAutoGrowSize) { this(null, null, autoGrowNullReferences, autoGrowCollections, maximumAutoGrowSize); } @@ -279,7 +287,9 @@ 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 + * @deprecated as of Spring Framework 7.1, in favor of the {@linkplain #builder() builder API} */ + @Deprecated(since = "7.1") public SpelParserConfiguration(@Nullable SpelCompilerMode compilerMode, @Nullable ClassLoader compilerClassLoader, boolean autoGrowNullReferences, boolean autoGrowCollections, int maximumAutoGrowSize) { @@ -308,7 +318,9 @@ public class SpelParserConfiguration { * @see #SPRING_EXPRESSION_MAX_OPERATIONS_PROPERTY_NAME * @see #SPRING_EXPRESSION_MAX_BIG_POWER_BITS_PROPERTY_NAME * @see #DEFAULT_MAX_EXPRESSION_NESTING_DEPTH + * @deprecated as of Spring Framework 7.1, in favor of the {@linkplain #builder() builder API} */ + @Deprecated(since = "7.1") public SpelParserConfiguration(@Nullable SpelCompilerMode compilerMode, @Nullable ClassLoader compilerClassLoader, boolean autoGrowNullReferences, boolean autoGrowCollections, int maximumAutoGrowSize, int maximumExpressionLength) { @@ -337,7 +349,9 @@ public class SpelParserConfiguration { * @since 6.2.19 * @see #SPRING_EXPRESSION_MAX_BIG_POWER_BITS_PROPERTY_NAME * @see #DEFAULT_MAX_EXPRESSION_NESTING_DEPTH + * @deprecated as of Spring Framework 7.1, in favor of the {@linkplain #builder() builder API} */ + @Deprecated(since = "7.1") public SpelParserConfiguration(SpelCompilerMode compilerMode, @Nullable ClassLoader compilerClassLoader, boolean autoGrowNullReferences, boolean autoGrowCollections, int maximumAutoGrowSize, int maximumExpressionLength, int maximumOperations) { @@ -369,7 +383,9 @@ public class SpelParserConfiguration { * operation; must be a positive number; use {@link Integer#MAX_VALUE} for no limit * @since 7.0.9 * @see #DEFAULT_MAX_EXPRESSION_NESTING_DEPTH + * @deprecated as of Spring Framework 7.1, in favor of the {@linkplain #builder() builder API} */ + @Deprecated(since = "7.1") public SpelParserConfiguration(SpelCompilerMode compilerMode, @Nullable ClassLoader compilerClassLoader, boolean autoGrowNullReferences, boolean autoGrowCollections, int maximumAutoGrowSize, int maximumExpressionLength, int maximumOperations, int maximumBigPowerBits) { @@ -402,7 +418,9 @@ public class SpelParserConfiguration { * @param maximumNestingDepth the maximum nesting depth permitted within a SpEL * expression; must be a positive number * @since 7.1 + * @deprecated as of Spring Framework 7.1, in favor of the {@linkplain #builder() builder API} */ + @Deprecated(since = "7.1") public SpelParserConfiguration(SpelCompilerMode compilerMode, @Nullable ClassLoader compilerClassLoader, boolean autoGrowNullReferences, boolean autoGrowCollections, int maximumAutoGrowSize, int maximumExpressionLength, int maximumOperations, int maximumBigPowerBits, int maximumNestingDepth) { @@ -690,6 +708,7 @@ public class SpelParserConfiguration { /** * Build the {@link SpelParserConfiguration} configured via this builder. */ + @SuppressWarnings("deprecation") public SpelParserConfiguration build() { int maximumOperations = (this.maximumOperations != null ? this.maximumOperations : retrieveMaxOperations()); diff --git a/spring-expression/src/test/java/org/springframework/expression/spel/IndexingTests.java b/spring-expression/src/test/java/org/springframework/expression/spel/IndexingTests.java index efafcb43aeb..f02d7e0d046 100644 --- a/spring-expression/src/test/java/org/springframework/expression/spel/IndexingTests.java +++ b/spring-expression/src/test/java/org/springframework/expression/spel/IndexingTests.java @@ -340,7 +340,8 @@ class IndexingTests { @Test void defaultMaxAutoGrowSizeMatchesDataBinderDefault() { - SpelParserConfiguration configuration = new SpelParserConfiguration(true, true); + SpelParserConfiguration configuration = + SpelParserConfiguration.builder().autoGrowNullReferences().autoGrowCollections().build(); assertThat(configuration.getMaximumAutoGrowSize()) .isEqualTo(SpelParserConfiguration.DEFAULT_MAX_AUTO_GROW_SIZE) .isEqualTo(256); @@ -349,7 +350,8 @@ class IndexingTests { @Test void zeroMaximumAutoGrowSizeIsAllowedAndDisablesGrowth() { decimals = new ArrayList<>(); - SpelParserConfiguration configuration = new SpelParserConfiguration(true, true, 0); + SpelParserConfiguration configuration = SpelParserConfiguration.builder() + .autoGrowNullReferences().autoGrowCollections().maximumAutoGrowSize(0).build(); SpelExpressionParser parser = new SpelExpressionParser(configuration); Expression indexExpression = parser.parseExpression("decimals[0]"); @@ -361,14 +363,15 @@ class IndexingTests { @Test void negativeMaximumAutoGrowSizeIsRejected() { assertThatIllegalArgumentException() - .isThrownBy(() -> new SpelParserConfiguration(true, true, -1)) + .isThrownBy(() -> SpelParserConfiguration.builder().maximumAutoGrowSize(-1)) .withMessage("'maximumAutoGrowSize' must not be negative"); } @Test void collectionGrowsUpToDefaultMaxAutoGrowSizeButNotBeyond() { decimals = new ArrayList<>(); - SpelExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true)); + SpelExpressionParser parser = new SpelExpressionParser( + SpelParserConfiguration.builder().autoGrowNullReferences().autoGrowCollections().build()); // Growing up to the default maximum auto-grow size should succeed. parser.parseExpression("decimals[255]").getValue(IndexingTests.this); @@ -384,7 +387,8 @@ class IndexingTests { @Test void collectionCanGrowBeyondDefaultMaxAutoGrowSizeWhenConfiguredExplicitly() { decimals = new ArrayList<>(); - SpelParserConfiguration configuration = new SpelParserConfiguration(true, true, 1000); + SpelParserConfiguration configuration = SpelParserConfiguration.builder() + .autoGrowNullReferences().autoGrowCollections().maximumAutoGrowSize(1000).build(); SpelExpressionParser parser = new SpelExpressionParser(configuration); parser.parseExpression("decimals[500]").getValue(IndexingTests.this); diff --git a/spring-expression/src/test/java/org/springframework/expression/spel/SpelParserConfigurationTests.java b/spring-expression/src/test/java/org/springframework/expression/spel/SpelParserConfigurationTests.java index 6ef140ac101..a3a9bc8c84b 100644 --- a/spring-expression/src/test/java/org/springframework/expression/spel/SpelParserConfigurationTests.java +++ b/spring-expression/src/test/java/org/springframework/expression/spel/SpelParserConfigurationTests.java @@ -31,6 +31,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException class SpelParserConfigurationTests { @Test + @SuppressWarnings("deprecation") void builderAppliesSameDefaultsAsNoArgConstructor() { SpelParserConfiguration expected = new SpelParserConfiguration(); SpelParserConfiguration actual = SpelParserConfiguration.builder().build(); @@ -110,6 +111,7 @@ class SpelParserConfigurationTests { * these constructors confined to this class. */ @Nested + @SuppressWarnings("deprecation") class LegacyConstructorTests { @Test diff --git a/spring-expression/src/test/java/org/springframework/expression/spel/standard/SpelParserTests.java b/spring-expression/src/test/java/org/springframework/expression/spel/standard/SpelParserTests.java index e1451bbd3f3..7372970e351 100644 --- a/spring-expression/src/test/java/org/springframework/expression/spel/standard/SpelParserTests.java +++ b/spring-expression/src/test/java/org/springframework/expression/spel/standard/SpelParserTests.java @@ -65,7 +65,8 @@ class SpelParserTests { private final int maxNestingDepth = 10; - private final SpelExpressionParser parser = new SpelExpressionParser(configurationWithMaxNestingDepth(maxNestingDepth)); + private final SpelExpressionParser parser = new SpelExpressionParser( + SpelParserConfiguration.builder().maximumNestingDepth(maxNestingDepth).build()); @Test @@ -103,10 +104,10 @@ class SpelParserTests { void maxNestingDepthProtectsAgainstStackOverflowFromChainedUnaryOperators() { // Effectively disable the expression-length limit so that the nesting-depth // limit is the guard that stops the parser well before the JVM call stack does. - SpelParserConfiguration configuration = new SpelParserConfiguration(SpelCompilerMode.OFF, null, false, false, - 0, Integer.MAX_VALUE, SpelParserConfiguration.DEFAULT_MAX_OPERATIONS, - SpelParserConfiguration.DEFAULT_MAX_BIG_POWER_BITS, - SpelParserConfiguration.DEFAULT_MAX_EXPRESSION_NESTING_DEPTH); + SpelParserConfiguration configuration = SpelParserConfiguration.builder() + .compilerMode(SpelCompilerMode.OFF) + .maximumExpressionLength(Integer.MAX_VALUE) + .build(); SpelExpressionParser parser = new SpelExpressionParser(configuration); assertParseExceptionThrownBy(() -> parser.parseExpression("!".repeat(100_000) + "true")) @@ -129,12 +130,6 @@ class SpelParserTests { assertNestingDepthExceeded(() -> parser.parseExpression(nestedTernaryExpression(50)), maxNestingDepth); } - private static SpelParserConfiguration configurationWithMaxNestingDepth(int maxNestingDepth) { - return new SpelParserConfiguration(SpelCompilerMode.OFF, null, false, false, 0, 10_000, - SpelParserConfiguration.DEFAULT_MAX_OPERATIONS, SpelParserConfiguration.DEFAULT_MAX_BIG_POWER_BITS, - maxNestingDepth); - } - private static void assertNestingDepthExceeded(ThrowingCallable throwingCallable, int maxNestingDepth) { assertParseExceptionThrownBy(throwingCallable) .withMessageEndingWith("SpEL expression nesting depth exceeds the threshold of " + maxNestingDepth)