Merge branch '7.0.x'

This commit is contained in:
Sam Brannen
2026-08-22 12:09:56 +02:00
11 changed files with 666 additions and 151 deletions
@@ -488,17 +488,23 @@ Kotlin::
It is possible to configure the SpEL expression parser by using a parser configuration
object (`org.springframework.expression.spel.SpelParserConfiguration`). The configuration
object controls the behavior of some of the expression components. For example, if you
index into a collection and the element at the specified index is `null`, SpEL can
automatically create the element. This is useful when using expressions made up of a
chain of property references. Similarly, if you index into a collection and specify an
index that is greater than the current size of the collection, SpEL can automatically
grow the collection to accommodate that index. In order to add an element at the
specified index, SpEL will try to create the element using the element type's default
constructor before setting the specified value. If the element type does not have a
default constructor, `null` will be added to the collection. If there is no built-in
converter or custom converter that knows how to set the value, `null` will remain in the
collection at the specified index. The following example demonstrates how to
object controls the behavior of some of the expression components. To create a
`SpelParserConfiguration` instance, favor `SpelParserConfiguration.builder()` over the
numerous constructors in `SpelParserConfiguration`, since the builder only requires
configuration of the properties that need to deviate from their sensible defaults --
or use `SpelParserConfiguration.withDefaults()` if none of those defaults need to be
overridden.
For example, if you index into a collection and the element at the specified index is
`null`, SpEL can automatically create the element. This is useful when using expressions
made up of a chain of property references. Similarly, if you index into a collection and
specify an index that is greater than the current size of the collection, SpEL can
automatically grow the collection to accommodate that index. In order to add an element
at the specified index, SpEL will try to create the element using the element type's
default constructor before setting the specified value. If the element type does not
have a default constructor, `null` will be added to the collection. If there is no
built-in converter or custom converter that knows how to set the value, `null` will
remain in the collection at the specified index. The following example demonstrates how to
automatically grow a `List`.
[tabs]
@@ -511,10 +517,10 @@ Java::
public List<String> list;
}
// Turn on:
// - auto null reference initialization
// - auto collection growing
SpelParserConfiguration config = new SpelParserConfiguration(true, true);
SpelParserConfiguration config = SpelParserConfiguration.builder()
.autoGrowNullReferences()
.autoGrowCollections()
.build();
ExpressionParser parser = new SpelExpressionParser(config);
@@ -536,10 +542,10 @@ Kotlin::
var list: List<String>? = null
}
// Turn on:
// - auto null reference initialization
// - auto collection growing
val config = SpelParserConfiguration(true, true)
val config = SpelParserConfiguration.builder()
.autoGrowNullReferences()
.autoGrowCollections()
.build()
val parser = SpelExpressionParser(config)
@@ -564,7 +570,8 @@ create a `SpelExpressionParser` programmatically, you can specify a custom
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
programmatically, you can specify a custom `maxExpressionLength` via
`SpelParserConfiguration.builder().maximumExpressionLength(...)` when creating the
`SpelParserConfiguration` that you provide to the `SpelExpressionParser`. If you wish to
set the `maxExpressionLength` used for parsing SpEL expressions within an
`ApplicationContext` -- for example, in XML bean definitions, `@Value`, etc. -- you can
@@ -575,12 +582,14 @@ xref:appendix.adoc#appendix-spring-properties[Supported Spring Properties]).
Similarly, the number of operations performed during the evaluation of a SpEL expression
cannot exceed 10,000 by default; however, the `maxOperations` value is configurable. If
you create a `SpelExpressionParser` programmatically (the recommend approach), you can
specify a custom `maxOperations` value when creating the `SpelParserConfiguration` that
you provide to the `SpelExpressionParser`. If you are not able to configure an explicit
value for `maxOperations` via `SpelParserConfiguration`, you can set a JVM system
property or Spring property named `spring.expression.maxOperations` to the maximum number
of operations required by your application (see
xref:appendix.adoc#appendix-spring-properties[Supported Spring Properties]).
specify a custom `maxOperations` value via
`SpelParserConfiguration.builder().maximumOperations(...)` when creating the
`SpelParserConfiguration` that you provide to the `SpelExpressionParser`. If you are not
able to configure an explicit value for `maxOperations` via `SpelParserConfiguration`,
you can set a JVM system property or Spring property named
`spring.expression.maxOperations` to the maximum number of operations required by your
application (see xref:appendix.adoc#appendix-spring-properties[Supported Spring
Properties]).
In addition, the result of a `BigDecimal` or `BigInteger` power operation within a SpEL
expression cannot exceed 1,000,000 bits by default approximately equivalent to a
@@ -588,19 +597,21 @@ decimal number with 300,000 digits. Power operations involving large base values
exponents can be computationally expensive, and this limit ensures that evaluations
remain bounded; however, the `maximumBigPowerBits` value is configurable. If you create a
`SpelExpressionParser` programmatically (the recommended approach), you can specify a
custom `maximumBigPowerBits` value when creating the `SpelParserConfiguration` that you
provide to the `SpelExpressionParser`. To remove this limit entirely, pass
`Integer.MAX_VALUE` as the `maximumBigPowerBits` value. If you are not able to configure
an explicit value for `maximumBigPowerBits` via `SpelParserConfiguration`, you can set a
JVM system property or Spring property named `spring.expression.maxBigPowerBits` to the
maximum result size in bits (see xref:appendix.adoc#appendix-spring-properties[Supported
Spring Properties]).
custom `maximumBigPowerBits` value via
`SpelParserConfiguration.builder().maximumBigPowerBits(...)` when creating the
`SpelParserConfiguration` that you provide to the `SpelExpressionParser`. To remove this
limit entirely, pass `Integer.MAX_VALUE` as the `maximumBigPowerBits` value. If you are
not able to configure an explicit value for `maximumBigPowerBits` via
`SpelParserConfiguration`, you can set a JVM system property or Spring property named
`spring.expression.maxBigPowerBits` to the maximum result size in bits (see
xref:appendix.adoc#appendix-spring-properties[Supported Spring Properties]).
Likewise, the structural nesting depth of a SpEL expression -- for example, the depth of
nested inline lists or maps, parenthesized expressions, ternary or Elvis expressions, or
chained unary operators -- cannot exceed 1,000 by default; however, the
`maximumNestingDepth` value is configurable. If you create a `SpelExpressionParser`
programmatically, you can specify a custom `maximumNestingDepth` value when creating the
programmatically, you can specify a custom `maximumNestingDepth` value via
`SpelParserConfiguration.builder().maximumNestingDepth(...)` when creating the
`SpelParserConfiguration` that you provide to the `SpelExpressionParser`. Unlike
`maxExpressionLength` and `maxOperations`, there is currently no JVM system property or
Spring property available for configuring `maximumNestingDepth` globally.
@@ -702,8 +713,10 @@ Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
SpelParserConfiguration config = new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE,
this.getClass().getClassLoader());
SpelParserConfiguration config = SpelParserConfiguration.builder()
.compilerMode(SpelCompilerMode.IMMEDIATE)
.compilerClassLoader(getClass().getClassLoader())
.build();
SpelExpressionParser parser = new SpelExpressionParser(config);
@@ -718,8 +731,10 @@ Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
val config = SpelParserConfiguration(SpelCompilerMode.IMMEDIATE,
this.javaClass.classLoader)
val config = SpelParserConfiguration.builder()
.compilerMode(SpelCompilerMode.IMMEDIATE)
.compilerClassLoader(javaClass.classLoader)
.build()
val parser = SpelExpressionParser(config)
@@ -118,8 +118,10 @@ public class StandardBeanExpressionResolver implements BeanExpressionResolver {
* @param beanClassLoader the factory's bean class loader
*/
public StandardBeanExpressionResolver(@Nullable ClassLoader beanClassLoader) {
SpelParserConfiguration parserConfig = new SpelParserConfiguration(
null, beanClassLoader, false, false, Integer.MAX_VALUE, retrieveMaxExpressionLength());
SpelParserConfiguration parserConfig = SpelParserConfiguration.builder()
.compilerClassLoader(beanClassLoader)
.maximumExpressionLength(retrieveMaxExpressionLength())
.build();
this.expressionParser = new SpelExpressionParser(parserConfig);
}
@@ -75,7 +75,7 @@ public class ExpressionState {
public ExpressionState(EvaluationContext context) {
this(context, context.getRootObject(), new SpelParserConfiguration(false, false));
this(context, context.getRootObject(), SpelParserConfiguration.withDefaults());
}
public ExpressionState(EvaluationContext context, SpelParserConfiguration configuration) {
@@ -83,7 +83,7 @@ public class ExpressionState {
}
public ExpressionState(EvaluationContext context, TypedValue rootObject) {
this(context, rootObject, new SpelParserConfiguration(false, false));
this(context, rootObject, SpelParserConfiguration.withDefaults());
}
public ExpressionState(EvaluationContext context, TypedValue rootObject, SpelParserConfiguration configuration) {
@@ -27,12 +27,22 @@ import org.springframework.util.StringUtils;
/**
* Configuration object for the SpEL expression parser.
*
* <p>Rather than using one of the numerous constructors in this class, it is
* strongly recommended that you use the {@linkplain #builder() builder API} to
* configure and create a {@code SpelParserConfiguration} instance, since the
* builder only requires configuration of the properties that need to deviate from
* their sensible defaults &mdash; 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.
*
* @author Juergen Hoeller
* @author Phillip Webb
* @author Andy Clement
* @author Sam Brannen
* @since 3.0
* @see org.springframework.expression.spel.standard.SpelExpressionParser#SpelExpressionParser(SpelParserConfiguration)
* @see #withDefaults()
* @see #builder()
*/
public class SpelParserConfiguration {
@@ -95,10 +105,10 @@ public class SpelParserConfiguration {
/**
* System property to configure the default compiler mode for SpEL expression parsers: {@value}.
* <p><strong>NOTE</strong>: Instead of relying on a global default, applications
* and frameworks should ideally set an explicit custom value via the
* {@link #SpelParserConfiguration(SpelCompilerMode, ClassLoader, boolean, boolean, int, int, int, int, int)}
* constructor which provides complete configuration control and the ability
* to override global defaults per use case.
* and frameworks should ideally set an explicit custom value via
* {@link Builder#compilerMode(SpelCompilerMode)}, which provides complete
* configuration control and the ability to override global defaults per
* use case.
* <p>Can also be configured via the {@link SpringProperties} mechanism.
*/
public static final String SPRING_EXPRESSION_COMPILER_MODE_PROPERTY_NAME = "spring.expression.compiler.mode";
@@ -107,10 +117,10 @@ public class SpelParserConfiguration {
* System property to configure the default maximum number of operations permitted
* during SpEL expression evaluation: {@value}.
* <p><strong>NOTE</strong>: Instead of relying on a global default, applications
* and frameworks should ideally set an explicit custom value via the
* {@link #SpelParserConfiguration(SpelCompilerMode, ClassLoader, boolean, boolean, int, int, int, int, int)}
* constructor which provides complete configuration control and the ability
* to override global defaults per use case.
* and frameworks should ideally set an explicit custom value via
* {@link Builder#maximumOperations(int)}, which provides complete
* configuration control and the ability to override global defaults per
* use case.
* <p>Can also be configured via the {@link SpringProperties} mechanism.
* @since 6.2.19
* @see #DEFAULT_MAX_OPERATIONS
@@ -122,10 +132,10 @@ public class SpelParserConfiguration {
* result of a {@link java.math.BigDecimal} or {@link java.math.BigInteger} power
* operation within a SpEL expression: {@value}.
* <p><strong>NOTE</strong>: Instead of relying on a global default, applications
* and frameworks should ideally set an explicit custom value via the
* {@link #SpelParserConfiguration(SpelCompilerMode, ClassLoader, boolean, boolean, int, int, int, int, int)}
* constructor which provides complete configuration control and the ability
* to override global defaults per use case.
* and frameworks should ideally set an explicit custom value via
* {@link Builder#maximumBigPowerBits(int)}, which provides complete
* configuration control and the ability to override global defaults per
* use case.
* <p>Can also be configured via the {@link SpringProperties} mechanism.
* @since 7.0.9
* @see #DEFAULT_MAX_BIG_POWER_BITS
@@ -162,12 +172,35 @@ public class SpelParserConfiguration {
private final int maximumNestingDepth;
/**
* Create a new {@code SpelParserConfiguration} instance with the same defaults
* applied by {@link #builder()}.
* <p>This is shorthand for {@code SpelParserConfiguration.builder().build()},
* for use whenever none of the defaults need to be overridden.
* @since 7.0.10
* @see #builder()
*/
public static SpelParserConfiguration withDefaults() {
return builder().build();
}
/**
* Create a new {@link Builder} for configuring a {@code SpelParserConfiguration}.
* <p>The builder only requires configuration of the properties that need
* to deviate from their sensible defaults. See {@link Builder} for details
* on those defaults.
* @since 7.0.10
* @see #withDefaults()
*/
public static Builder builder() {
return new Builder();
}
/**
* Create a new {@code SpelParserConfiguration} instance with default settings.
* <p><strong>NOTE</strong>: Favor the
* {@link #SpelParserConfiguration(SpelCompilerMode, ClassLoader, boolean, boolean, int, int, int, int, int)}
* constructor for complete configuration control and the ability to override
* global defaults per use case.
* <p><strong>NOTE</strong>: Favor {@link #builder()} for complete
* configuration control and the ability to override global defaults
* per use case.
* @see #SPRING_EXPRESSION_COMPILER_MODE_PROPERTY_NAME
* @see #SPRING_EXPRESSION_MAX_OPERATIONS_PROPERTY_NAME
* @see #SPRING_EXPRESSION_MAX_BIG_POWER_BITS_PROPERTY_NAME
@@ -179,10 +212,9 @@ public class SpelParserConfiguration {
/**
* Create a new {@code SpelParserConfiguration} instance.
* <p><strong>NOTE</strong>: Favor the
* {@link #SpelParserConfiguration(SpelCompilerMode, ClassLoader, boolean, boolean, int, int, int, int, int)}
* constructor for complete configuration control and the ability to override
* global defaults per use case.
* <p><strong>NOTE</strong>: Favor {@link #builder()} for complete
* configuration control and the ability to override global defaults
* per use case.
* @param compilerMode the compiler mode that parsers using this configuration
* should use; or {@code null} to use the default mode
* @param compilerClassLoader the {@code ClassLoader} to use as the basis for
@@ -198,10 +230,9 @@ public class SpelParserConfiguration {
/**
* Create a new {@code SpelParserConfiguration} instance.
* <p><strong>NOTE</strong>: Favor the
* {@link #SpelParserConfiguration(SpelCompilerMode, ClassLoader, boolean, boolean, int, int, int, int, int)}
* constructor for complete configuration control and the ability to override
* global defaults per use case.
* <p><strong>NOTE</strong>: Favor {@link #builder()} for complete
* configuration control and the ability to override global defaults
* per use case.
* @param autoGrowNullReferences if null references should automatically grow
* @param autoGrowCollections if collections should automatically grow
* @see #SPRING_EXPRESSION_COMPILER_MODE_PROPERTY_NAME
@@ -215,10 +246,9 @@ public class SpelParserConfiguration {
/**
* Create a new {@code SpelParserConfiguration} instance.
* <p><strong>NOTE</strong>: Favor the
* {@link #SpelParserConfiguration(SpelCompilerMode, ClassLoader, boolean, boolean, int, int, int, int, int)}
* constructor for complete configuration control and the ability to override
* global defaults per use case.
* <p><strong>NOTE</strong>: Favor {@link #builder()} for complete
* configuration control and the ability to override 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;
@@ -234,10 +264,9 @@ public class SpelParserConfiguration {
/**
* Create a new {@code SpelParserConfiguration} instance.
* <p><strong>NOTE</strong>: Favor the
* {@link #SpelParserConfiguration(SpelCompilerMode, ClassLoader, boolean, boolean, int, int, int, int, int)}
* constructor for complete configuration control and the ability to override
* global defaults per use case.
* <p><strong>NOTE</strong>: Favor {@link #builder()} for complete
* configuration control and the ability to override global defaults
* per use case.
* @param compilerMode the compiler mode that parsers using this configuration
* should use; or {@code null} to use the default mode
* @param compilerClassLoader the {@code ClassLoader} to use as the basis for
@@ -260,10 +289,9 @@ public class SpelParserConfiguration {
/**
* Create a new {@code SpelParserConfiguration} instance.
* <p><strong>NOTE</strong>: Favor the
* {@link #SpelParserConfiguration(SpelCompilerMode, ClassLoader, boolean, boolean, int, int, int, int, int)}
* constructor for complete configuration control and the ability to override
* global defaults per use case.
* <p><strong>NOTE</strong>: Favor {@link #builder()} for complete
* configuration control and the ability to override global defaults
* per use case.
* @param compilerMode the compiler mode that parsers using this configuration
* should use; or {@code null} to use the default mode
* @param compilerClassLoader the {@code ClassLoader} to use as the basis for
@@ -290,10 +318,9 @@ public class SpelParserConfiguration {
/**
* Create a new {@code SpelParserConfiguration} instance.
* <p><strong>NOTE</strong>: Favor the
* {@link #SpelParserConfiguration(SpelCompilerMode, ClassLoader, boolean, boolean, int, int, int, int, int)}
* constructor for complete configuration control and the ability to override
* global defaults per use case.
* <p><strong>NOTE</strong>: Favor {@link #builder()} for complete
* configuration control and the ability to override global defaults
* per use case.
* @param compilerMode the compiler mode that parsers using this configuration
* should use; must not be {@code null}
* @param compilerClassLoader the {@code ClassLoader} to use as the basis for
@@ -321,10 +348,9 @@ public class SpelParserConfiguration {
/**
* Create a new {@code SpelParserConfiguration} instance.
* <p><strong>NOTE</strong>: Favor the
* {@link #SpelParserConfiguration(SpelCompilerMode, ClassLoader, boolean, boolean, int, int, int, int, int)}
* constructor for complete configuration control and the ability to override
* global defaults per use case.
* <p><strong>NOTE</strong>: Favor {@link #builder()} for complete
* configuration control and the ability to override global defaults
* per use case.
* @param compilerMode the compiler mode that parsers using this configuration
* should use; must not be {@code null}
* @param compilerClassLoader the {@code ClassLoader} to use as the basis for
@@ -354,6 +380,9 @@ public class SpelParserConfiguration {
/**
* Create a new {@code SpelParserConfiguration} instance.
* <p><strong>NOTE</strong>: Favor {@link #builder()} for complete
* configuration control and the ability to override global defaults
* per use case.
* @param compilerMode the compiler mode that parsers using this configuration
* should use; must not be {@code null}
* @param compilerClassLoader the {@code ClassLoader} to use as the basis for
@@ -504,4 +533,173 @@ public class SpelParserConfiguration {
}
}
/**
* Fluent builder API for {@link SpelParserConfiguration}.
* <p>Each property defaults to the same value as the corresponding
* property in a {@code SpelParserConfiguration} created via the
* {@linkplain SpelParserConfiguration#SpelParserConfiguration() no-arg constructor}
* &mdash; including honoring the system properties or Spring properties named
* {@value #SPRING_EXPRESSION_COMPILER_MODE_PROPERTY_NAME},
* {@value #SPRING_EXPRESSION_MAX_OPERATIONS_PROPERTY_NAME}, and
* {@value #SPRING_EXPRESSION_MAX_BIG_POWER_BITS_PROPERTY_NAME}.
* <p>Only configure the properties that need to deviate from those defaults.
* @since 7.0.10
* @see SpelParserConfiguration#builder()
*/
public static final class Builder {
private SpelCompilerMode compilerMode = defaultCompilerMode;
private @Nullable ClassLoader compilerClassLoader;
private boolean autoGrowNullReferences = false;
private boolean autoGrowCollections = false;
private int maximumAutoGrowSize = DEFAULT_MAX_AUTO_GROW_SIZE;
private int maximumExpressionLength = DEFAULT_MAX_EXPRESSION_LENGTH;
private @Nullable Integer maximumOperations;
private @Nullable Integer maximumBigPowerBits;
private int maximumNestingDepth = DEFAULT_MAX_EXPRESSION_NESTING_DEPTH;
private Builder() {
}
/**
* Set the compiler mode that parsers using this configuration should use.
* <p>By default, set to the value configured via {@link SpringProperties}
* for a system property or Spring property named
* {@value #SPRING_EXPRESSION_COMPILER_MODE_PROPERTY_NAME}, or
* {@link SpelCompilerMode#OFF} if that property is not set.
* @param compilerMode the compiler mode to use; must not be {@code null}
*/
public Builder compilerMode(SpelCompilerMode compilerMode) {
Assert.notNull(compilerMode, "'compilerMode' must not be null");
this.compilerMode = compilerMode;
return this;
}
/**
* Set the {@code ClassLoader} to use as the basis for expression compilation.
* <p>By default, set to {@code null}, indicating that the default
* {@code ClassLoader} should be used.
* @param compilerClassLoader the {@code ClassLoader} to use
*/
public Builder compilerClassLoader(@Nullable ClassLoader compilerClassLoader) {
this.compilerClassLoader = compilerClassLoader;
return this;
}
/**
* Enable automatic growth of {@code null} references encountered while
* traversing a property path.
* <p>By default, this is disabled.
*/
public Builder autoGrowNullReferences() {
this.autoGrowNullReferences = true;
return this;
}
/**
* Enable automatic growth of collections and arrays encountered while
* traversing a property path.
* <p>By default, this is disabled.
* @see #maximumAutoGrowSize(int)
*/
public Builder autoGrowCollections() {
this.autoGrowCollections = true;
return this;
}
/**
* Set the maximum size to which a collection or array can automatically grow.
* <p>By default, set to {@link #DEFAULT_MAX_AUTO_GROW_SIZE}.
* @param maximumAutoGrowSize the maximum auto-grow size; must not be
* negative, and a value of {@code 0} effectively disables growing a
* collection or array beyond its current size
* @see #autoGrowCollections()
*/
public Builder maximumAutoGrowSize(int maximumAutoGrowSize) {
Assert.isTrue(maximumAutoGrowSize >= 0, "'maximumAutoGrowSize' must not be negative");
this.maximumAutoGrowSize = maximumAutoGrowSize;
return this;
}
/**
* Set the maximum length permitted for a SpEL expression.
* <p>By default, set to {@link #DEFAULT_MAX_EXPRESSION_LENGTH}.
* @param maximumExpressionLength the maximum expression length; must be
* a positive number
*/
public Builder maximumExpressionLength(int maximumExpressionLength) {
Assert.isTrue(maximumExpressionLength > 0, "'maximumExpressionLength' must be a positive number");
this.maximumExpressionLength = maximumExpressionLength;
return this;
}
/**
* Set the maximum number of operations permitted during SpEL expression evaluation.
* <p>By default, set to the value configured via {@link SpringProperties}
* for a system property or Spring property named
* {@value #SPRING_EXPRESSION_MAX_OPERATIONS_PROPERTY_NAME}, or
* {@link #DEFAULT_MAX_OPERATIONS} if that property is not set.
* @param maximumOperations the maximum number of operations; must be a
* positive number
*/
public Builder maximumOperations(int maximumOperations) {
Assert.isTrue(maximumOperations > 0, "'maximumOperations' must be a positive number");
this.maximumOperations = maximumOperations;
return this;
}
/**
* Set the maximum number of bits permitted in the result of a
* {@link java.math.BigDecimal} or {@link java.math.BigInteger} power
* operation within a SpEL expression.
* <p>By default, set to the value configured via {@link SpringProperties}
* for a system property or Spring property named
* {@value #SPRING_EXPRESSION_MAX_BIG_POWER_BITS_PROPERTY_NAME}, or
* {@link #DEFAULT_MAX_BIG_POWER_BITS} if that property is not set.
* @param maximumBigPowerBits the maximum number of bits; must be a
* positive number; use {@link Integer#MAX_VALUE} for no limit
*/
public Builder maximumBigPowerBits(int maximumBigPowerBits) {
Assert.isTrue(maximumBigPowerBits > 0, "'maximumBigPowerBits' must be a positive number");
this.maximumBigPowerBits = maximumBigPowerBits;
return this;
}
/**
* Set the maximum nesting depth permitted within a SpEL expression.
* <p>By default, set to {@link #DEFAULT_MAX_EXPRESSION_NESTING_DEPTH}.
* @param maximumNestingDepth the maximum nesting depth; must be a
* positive number
* @since 7.1
*/
public Builder maximumNestingDepth(int maximumNestingDepth) {
Assert.isTrue(maximumNestingDepth > 0, "'maximumNestingDepth' must be a positive number");
this.maximumNestingDepth = maximumNestingDepth;
return this;
}
/**
* Build the {@link SpelParserConfiguration} configured via this builder.
*/
public SpelParserConfiguration build() {
int maximumOperations = (this.maximumOperations != null ?
this.maximumOperations : retrieveMaxOperations());
int maximumBigPowerBits = (this.maximumBigPowerBits != null ?
this.maximumBigPowerBits : retrieveMaxBigPowerBits());
return new SpelParserConfiguration(this.compilerMode, this.compilerClassLoader,
this.autoGrowNullReferences, this.autoGrowCollections, this.maximumAutoGrowSize,
this.maximumExpressionLength, maximumOperations, maximumBigPowerBits, this.maximumNestingDepth);
}
}
}
@@ -41,7 +41,7 @@ public class SpelExpressionParser extends TemplateAwareExpressionParser {
* Create a parser with default settings.
*/
public SpelExpressionParser() {
this.configuration = new SpelParserConfiguration();
this.configuration = SpelParserConfiguration.withDefaults();
}
/**
@@ -86,8 +86,9 @@ class EvaluationTests extends AbstractExpressionTests {
String expression = "'%s'".formatted("Y".repeat(19_998));
assertThat(expression).hasSize(maximumExpressionLength);
SpelParserConfiguration configuration =
new SpelParserConfiguration(null, null, false, false, 0, maximumExpressionLength);
SpelParserConfiguration configuration = SpelParserConfiguration.builder()
.maximumExpressionLength(maximumExpressionLength)
.build();
ExpressionParser parser = new SpelExpressionParser(configuration);
Expression expr = parser.parseExpression(expression);
@@ -111,8 +112,11 @@ class EvaluationTests extends AbstractExpressionTests {
Expression expr1 = parser.parseExpression(expression);
assertThat(expr1.getValue(Boolean.class)).isTrue();
SpelParserConfiguration configuration =
new SpelParserConfiguration(SpelCompilerMode.OFF, null, false, false, 0, maxLength, maxOperations);
SpelParserConfiguration configuration = SpelParserConfiguration.builder()
.compilerMode(SpelCompilerMode.OFF)
.maximumExpressionLength(maxLength)
.maximumOperations(maxOperations)
.build();
parser = new SpelExpressionParser(configuration);
Expression expr2 = parser.parseExpression(expression);
assertThatExceptionOfType(SpelEvaluationException.class)
@@ -167,13 +171,20 @@ class EvaluationTests extends AbstractExpressionTests {
SpringProperties.setProperty(SPRING_EXPRESSION_MAX_OPERATIONS_PROPERTY_NAME, "" + maxOperations / 2);
// maxOperations + 1 should override maxOperations / 2
SpelParserConfiguration configuration =
new SpelParserConfiguration(SpelCompilerMode.OFF, null, false, false, 0, maxLength, maxOperations + 1);
SpelParserConfiguration configuration = SpelParserConfiguration.builder()
.compilerMode(SpelCompilerMode.OFF)
.maximumExpressionLength(maxLength)
.maximumOperations(maxOperations + 1)
.build();
parser = new SpelExpressionParser(configuration);
expr1 = parser.parseExpression(expression);
assertThat(expr1.getValue(Boolean.class)).isTrue();
configuration = new SpelParserConfiguration(SpelCompilerMode.OFF, null, false, false, 0, maxLength, maxOperations);
configuration = SpelParserConfiguration.builder()
.compilerMode(SpelCompilerMode.OFF)
.maximumExpressionLength(maxLength)
.maximumOperations(maxOperations)
.build();
parser = new SpelExpressionParser(configuration);
Expression expr2 = parser.parseExpression(expression);
assertThatExceptionOfType(SpelEvaluationException.class)
@@ -191,7 +202,8 @@ class EvaluationTests extends AbstractExpressionTests {
@Test
void createListsOnAttemptToIndexNull01() throws EvaluationException, ParseException {
ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
ExpressionParser parser = new SpelExpressionParser(
SpelParserConfiguration.builder().autoGrowNullReferences().autoGrowCollections().build());
Expression e = parser.parseExpression("list[0]");
TestClass testClass = new TestClass();
@@ -213,7 +225,8 @@ class EvaluationTests extends AbstractExpressionTests {
void createMapsOnAttemptToIndexNull() {
TestClass testClass = new TestClass();
StandardEvaluationContext ctx = new StandardEvaluationContext(testClass);
ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
ExpressionParser parser = new SpelExpressionParser(
SpelParserConfiguration.builder().autoGrowNullReferences().autoGrowCollections().build());
Object o = parser.parseExpression("map['a']").getValue(ctx);
assertThat(o).isNull();
@@ -230,7 +243,8 @@ class EvaluationTests extends AbstractExpressionTests {
void createObjectsOnAttemptToReferenceNull() {
TestClass testClass = new TestClass();
StandardEvaluationContext ctx = new StandardEvaluationContext(testClass);
ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
ExpressionParser parser = new SpelExpressionParser(
SpelParserConfiguration.builder().autoGrowNullReferences().autoGrowCollections().build());
Object o = parser.parseExpression("wibble.bar").getValue(ctx);
assertThat(o).isEqualTo("hello");
@@ -385,7 +399,7 @@ class EvaluationTests extends AbstractExpressionTests {
void initializingCollectionElementsOnWrite() {
TestPerson person = new TestPerson();
EvaluationContext context = new StandardEvaluationContext(person);
SpelParserConfiguration config = new SpelParserConfiguration(true, true);
SpelParserConfiguration config = SpelParserConfiguration.builder().autoGrowNullReferences().autoGrowCollections().build();
ExpressionParser parser = new SpelExpressionParser(config);
Expression e = parser.parseExpression("name");
e.setValue(context, "Oleg");
@@ -452,7 +466,8 @@ class EvaluationTests extends AbstractExpressionTests {
// Add a new element to the list
StandardEvaluationContext ctx = new StandardEvaluationContext(instance);
ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
ExpressionParser parser = new SpelExpressionParser(
SpelParserConfiguration.builder().autoGrowNullReferences().autoGrowCollections().build());
Expression e = parser.parseExpression("listOfStrings[++index3]='def'");
e.getValue(ctx);
assertThat(instance.listOfStrings).hasSize(2);
@@ -460,7 +475,8 @@ class EvaluationTests extends AbstractExpressionTests {
// Check reference beyond end of collection
ctx = new StandardEvaluationContext(instance);
parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
parser = new SpelExpressionParser(
SpelParserConfiguration.builder().autoGrowNullReferences().autoGrowCollections().build());
e = parser.parseExpression("listOfStrings[0]");
String value = e.getValue(ctx, String.class);
assertThat(value).isEqualTo("abc");
@@ -473,7 +489,7 @@ class EvaluationTests extends AbstractExpressionTests {
// Now turn off growing and reference off the end
StandardEvaluationContext failCtx = new StandardEvaluationContext(instance);
parser = new SpelExpressionParser(new SpelParserConfiguration(false, false));
parser = new SpelExpressionParser(SpelParserConfiguration.withDefaults());
Expression failExp = parser.parseExpression("listOfStrings[3]");
assertThatExceptionOfType(SpelEvaluationException.class)
.isThrownBy(() -> failExp.getValue(failCtx, String.class))
@@ -484,7 +500,12 @@ class EvaluationTests extends AbstractExpressionTests {
void limitCollectionGrowing() {
TestClass instance = new TestClass();
StandardEvaluationContext ctx = new StandardEvaluationContext(instance);
SpelExpressionParser parser = new SpelExpressionParser( new SpelParserConfiguration(true, true, 3));
SpelParserConfiguration config = SpelParserConfiguration.builder()
.autoGrowNullReferences()
.autoGrowCollections()
.maximumAutoGrowSize(3)
.build();
SpelExpressionParser parser = new SpelExpressionParser(config);
Expression e = parser.parseExpression("foo[2]");
e.setValue(ctx, "2");
assertThat(instance.getFoo()).hasSize(3);
@@ -819,9 +840,12 @@ class EvaluationTests extends AbstractExpressionTests {
// Use a small limit (16 bits) to verify behavior in tests.
private static final int TEST_MAX_RESULT_BITS = 16;
private final SpelExpressionParser limitedParser = new SpelExpressionParser(
new SpelParserConfiguration(SpelCompilerMode.OFF, null, false, false,
0, 10, 10, TEST_MAX_RESULT_BITS));
private final SpelExpressionParser limitedParser = new SpelExpressionParser(SpelParserConfiguration.builder()
.compilerMode(SpelCompilerMode.OFF)
.maximumExpressionLength(10)
.maximumOperations(10)
.maximumBigPowerBits(TEST_MAX_RESULT_BITS)
.build());
@Test
@@ -1016,7 +1040,8 @@ class EvaluationTests extends AbstractExpressionTests {
void increment01root() {
Integer i = 42;
StandardEvaluationContext ctx = new StandardEvaluationContext(i);
ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
ExpressionParser parser = new SpelExpressionParser(
SpelParserConfiguration.builder().autoGrowNullReferences().autoGrowCollections().build());
Expression e = parser.parseExpression("#this++");
assertThat(i).isEqualTo(42);
assertThatExceptionOfType(SpelEvaluationException.class)
@@ -1028,7 +1053,8 @@ class EvaluationTests extends AbstractExpressionTests {
void increment02postfix() {
Spr9751 helper = new Spr9751();
StandardEvaluationContext ctx = new StandardEvaluationContext(helper);
ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
ExpressionParser parser = new SpelExpressionParser(
SpelParserConfiguration.builder().autoGrowNullReferences().autoGrowCollections().build());
Expression e;
// BigDecimal
@@ -1081,7 +1107,8 @@ class EvaluationTests extends AbstractExpressionTests {
void increment02prefix() {
Spr9751 helper = new Spr9751();
StandardEvaluationContext ctx = new StandardEvaluationContext(helper);
ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
ExpressionParser parser = new SpelExpressionParser(
SpelParserConfiguration.builder().autoGrowNullReferences().autoGrowCollections().build());
Expression e;
// BigDecimal
@@ -1134,7 +1161,8 @@ class EvaluationTests extends AbstractExpressionTests {
void increment03() {
Spr9751 helper = new Spr9751();
StandardEvaluationContext ctx = new StandardEvaluationContext(helper);
ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
ExpressionParser parser = new SpelExpressionParser(
SpelParserConfiguration.builder().autoGrowNullReferences().autoGrowCollections().build());
Expression e1 = parser.parseExpression("m()++");
assertThatExceptionOfType(SpelEvaluationException.class)
@@ -1151,7 +1179,8 @@ class EvaluationTests extends AbstractExpressionTests {
void increment04() {
Integer i = 42;
StandardEvaluationContext ctx = new StandardEvaluationContext(i);
ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
ExpressionParser parser = new SpelExpressionParser(
SpelParserConfiguration.builder().autoGrowNullReferences().autoGrowCollections().build());
Expression e1 = parser.parseExpression("++1");
assertThatExceptionOfType(SpelEvaluationException.class)
.isThrownBy(() -> e1.getValue(ctx, double.class))
@@ -1166,7 +1195,8 @@ class EvaluationTests extends AbstractExpressionTests {
void decrement01root() {
Integer i = 42;
StandardEvaluationContext ctx = new StandardEvaluationContext(i);
ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
ExpressionParser parser = new SpelExpressionParser(
SpelParserConfiguration.builder().autoGrowNullReferences().autoGrowCollections().build());
Expression e = parser.parseExpression("#this--");
assertThat(i).isEqualTo(42);
assertThatExceptionOfType(SpelEvaluationException.class)
@@ -1178,7 +1208,8 @@ class EvaluationTests extends AbstractExpressionTests {
void decrement02postfix() {
Spr9751 helper = new Spr9751();
StandardEvaluationContext ctx = new StandardEvaluationContext(helper);
ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
ExpressionParser parser = new SpelExpressionParser(
SpelParserConfiguration.builder().autoGrowNullReferences().autoGrowCollections().build());
Expression e;
// BigDecimal
@@ -1231,7 +1262,8 @@ class EvaluationTests extends AbstractExpressionTests {
void decrement02prefix() {
Spr9751 helper = new Spr9751();
StandardEvaluationContext ctx = new StandardEvaluationContext(helper);
ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
ExpressionParser parser = new SpelExpressionParser(
SpelParserConfiguration.builder().autoGrowNullReferences().autoGrowCollections().build());
Expression e;
// BigDecimal
@@ -1284,7 +1316,8 @@ class EvaluationTests extends AbstractExpressionTests {
void decrement03() {
Spr9751 helper = new Spr9751();
StandardEvaluationContext ctx = new StandardEvaluationContext(helper);
ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
ExpressionParser parser = new SpelExpressionParser(
SpelParserConfiguration.builder().autoGrowNullReferences().autoGrowCollections().build());
Expression e1 = parser.parseExpression("m()--");
assertThatExceptionOfType(SpelEvaluationException.class)
@@ -1301,7 +1334,8 @@ class EvaluationTests extends AbstractExpressionTests {
void decrement04() {
Integer i = 42;
StandardEvaluationContext ctx = new StandardEvaluationContext(i);
ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
ExpressionParser parser = new SpelExpressionParser(
SpelParserConfiguration.builder().autoGrowNullReferences().autoGrowCollections().build());
Expression e1 = parser.parseExpression("--1");
assertThatExceptionOfType(SpelEvaluationException.class)
.isThrownBy(() -> e1.getValue(ctx, Integer.class))
@@ -1317,7 +1351,8 @@ class EvaluationTests extends AbstractExpressionTests {
void incrementAndDecrementTogether() {
Spr9751 helper = new Spr9751();
StandardEvaluationContext ctx = new StandardEvaluationContext(helper);
ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
ExpressionParser parser = new SpelExpressionParser(
SpelParserConfiguration.builder().autoGrowNullReferences().autoGrowCollections().build());
Expression e;
// index1 is 2 at the start - the 'intArray[#root.index1++]' should not be evaluated twice!
@@ -1345,7 +1380,8 @@ class EvaluationTests extends AbstractExpressionTests {
void incrementAllNodeTypes() throws SecurityException, NoSuchMethodException {
Spr9751 helper = new Spr9751();
StandardEvaluationContext ctx = new StandardEvaluationContext(helper);
ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
ExpressionParser parser = new SpelExpressionParser(
SpelParserConfiguration.builder().autoGrowNullReferences().autoGrowCollections().build());
Expression e;
// BooleanLiteral
@@ -161,7 +161,8 @@ class IndexingTests {
@Test
void setPropertyContainingMapAutoGrow() {
SpelExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, false));
SpelExpressionParser parser = new SpelExpressionParser(
SpelParserConfiguration.builder().autoGrowNullReferences().build());
Expression expression = parser.parseExpression("parameterizedMap");
assertThat(expression.getValueTypeDescriptor(this)).asString()
.isEqualTo("java.util.Map<java.lang.Integer, java.lang.Integer>");
@@ -206,7 +207,8 @@ class IndexingTests {
void setGenericPropertyContainingListAutogrow() {
List<Integer> property = new ArrayList<>();
this.property = property;
SpelExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
SpelExpressionParser parser = new SpelExpressionParser(
SpelParserConfiguration.builder().autoGrowNullReferences().autoGrowCollections().build());
Expression expression = parser.parseExpression("property");
assertThat(expression.getValueTypeDescriptor(this)).asString()
.isEqualTo("@%s java.util.ArrayList<?>", FieldAnnotation.class.getCanonicalName());
@@ -221,7 +223,8 @@ class IndexingTests {
@Test
void autoGrowListOfElementsWithoutDefaultConstructor() {
this.decimals = new ArrayList<>();
SpelExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
SpelExpressionParser parser = new SpelExpressionParser(
SpelParserConfiguration.builder().autoGrowNullReferences().autoGrowCollections().build());
parser.parseExpression("decimals[0]").setValue(this, "123.4");
assertThat(decimals).containsExactly(BigDecimal.valueOf(123.4));
}
@@ -231,7 +234,8 @@ class IndexingTests {
this.decimals = new ArrayList<>();
this.decimals.add(null);
this.decimals.add(BigDecimal.ONE);
SpelExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
SpelExpressionParser parser = new SpelExpressionParser(
SpelParserConfiguration.builder().autoGrowNullReferences().autoGrowCollections().build());
parser.parseExpression("decimals[0]").setValue(this, "9876.5");
assertThat(decimals).containsExactly(BigDecimal.valueOf(9876.5), BigDecimal.ONE);
}
@@ -282,7 +286,8 @@ class IndexingTests {
@Test
void indexIntoGenericPropertyContainingNullList() {
SpelParserConfiguration configuration = new SpelParserConfiguration(true, true);
SpelParserConfiguration configuration =
SpelParserConfiguration.builder().autoGrowNullReferences().autoGrowCollections().build();
SpelExpressionParser parser = new SpelExpressionParser(configuration);
Expression expression = parser.parseExpression("property");
assertThat(expression.getValueTypeDescriptor(this)).asString()
@@ -299,7 +304,8 @@ class IndexingTests {
void indexIntoGenericPropertyContainingGrowingList() {
List<String> property = new ArrayList<>();
this.property = property;
SpelParserConfiguration configuration = new SpelParserConfiguration(true, true);
SpelParserConfiguration configuration =
SpelParserConfiguration.builder().autoGrowNullReferences().autoGrowCollections().build();
SpelExpressionParser parser = new SpelExpressionParser(configuration);
Expression expression = parser.parseExpression("property");
assertThat(expression.getValueTypeDescriptor(this)).asString()
@@ -316,7 +322,8 @@ class IndexingTests {
void indexIntoGenericPropertyContainingGrowingList2() {
List<String> property2 = new ArrayList<>();
this.property2 = property2;
SpelParserConfiguration configuration = new SpelParserConfiguration(true, true);
SpelParserConfiguration configuration =
SpelParserConfiguration.builder().autoGrowNullReferences().autoGrowCollections().build();
SpelExpressionParser parser = new SpelExpressionParser(configuration);
Expression expression = parser.parseExpression("property2");
assertThat(expression.getValueTypeDescriptor(this)).asString().isEqualTo("java.util.ArrayList<?>");
@@ -260,8 +260,10 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
@Test
void failsWhenSettingContextForExpression_SPR12326() {
SpelExpressionParser parser = new SpelExpressionParser(
new SpelParserConfiguration(SpelCompilerMode.OFF, getClass().getClassLoader()));
SpelExpressionParser parser = new SpelExpressionParser(SpelParserConfiguration.builder()
.compilerMode(SpelCompilerMode.OFF)
.compilerClassLoader(getClass().getClassLoader())
.build());
Person3 person = new Person3("foo", 1);
SpelExpression expression = parser.parseRaw("#it?.age?.equals([0])");
StandardEvaluationContext context = new StandardEvaluationContext(new Object[] {1});
@@ -1237,8 +1239,10 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
@Test
void opNe_SPR14863() {
SpelParserConfiguration configuration =
new SpelParserConfiguration(SpelCompilerMode.MIXED, ClassLoader.getSystemClassLoader());
SpelParserConfiguration configuration = SpelParserConfiguration.builder()
.compilerMode(SpelCompilerMode.MIXED)
.compilerClassLoader(ClassLoader.getSystemClassLoader())
.build();
SpelExpressionParser parser = new SpelExpressionParser(configuration);
Expression expression = parser.parseExpression("data['my-key'] != 'my-value'");
@@ -1312,7 +1316,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
@Test
void nullComparison_SPR22358() {
SpelParserConfiguration configuration = new SpelParserConfiguration(SpelCompilerMode.OFF, null);
SpelParserConfiguration configuration = SpelParserConfiguration.builder().compilerMode(SpelCompilerMode.OFF).build();
SpelExpressionParser parser = new SpelExpressionParser(configuration);
StandardEvaluationContext ctx = new StandardEvaluationContext();
ctx.setRootObject(new Reg(1));
@@ -3465,8 +3469,10 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
@Test
void compilationOfBasicNullSafeMethodReference() {
SpelExpressionParser parser = new SpelExpressionParser(
new SpelParserConfiguration(SpelCompilerMode.OFF, getClass().getClassLoader()));
SpelExpressionParser parser = new SpelExpressionParser(SpelParserConfiguration.builder()
.compilerMode(SpelCompilerMode.OFF)
.compilerClassLoader(getClass().getClassLoader())
.build());
SpelExpression expression = parser.parseRaw("#it?.equals(3)");
StandardEvaluationContext context = new StandardEvaluationContext(new Object[] {1});
context.setVariable("it", 3);
@@ -4137,8 +4143,10 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
assertThat(expression.getValue(context, Boolean.class)).isTrue();
// Variant of above more like what was in the bug report:
SpelExpressionParser parser = new SpelExpressionParser(
new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE, getClass().getClassLoader()));
SpelExpressionParser parser = new SpelExpressionParser(SpelParserConfiguration.builder()
.compilerMode(SpelCompilerMode.IMMEDIATE)
.compilerClassLoader(getClass().getClassLoader())
.build());
SpelExpression ex = parser.parseRaw("#it?.age.equals([0])");
context = new StandardEvaluationContext(new Object[] {person.getAge()});
@@ -5273,8 +5281,10 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
@Test
void indexIntoMap_SPR12045() {
SpelParserConfiguration config = new SpelParserConfiguration(
SpelCompilerMode.IMMEDIATE,getClass().getClassLoader());
SpelParserConfiguration config = SpelParserConfiguration.builder()
.compilerMode(SpelCompilerMode.IMMEDIATE)
.compilerClassLoader(getClass().getClassLoader())
.build();
SpelExpressionParser parser = new SpelExpressionParser(config);
expression = parser.parseExpression("headers[command]");
@@ -6074,7 +6084,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
@Test
void ternaryWithMapAccess() {
SpelParserConfiguration config = new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE, null);
SpelParserConfiguration config = SpelParserConfiguration.builder().compilerMode(SpelCompilerMode.IMMEDIATE).build();
SpelExpressionParser parser = new SpelExpressionParser(config);
expression = parser.parseExpression(
@@ -6111,7 +6121,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
@Test // gh-19758
void ternaryMiscellaneous() {
SpelParserConfiguration configuration = new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE, null);
SpelParserConfiguration configuration = SpelParserConfiguration.builder().compilerMode(SpelCompilerMode.IMMEDIATE).build();
Expression exp;
StandardEvaluationContext context = new StandardEvaluationContext();
context.setVariable("map", Map.of("foo", "qux"));
@@ -6230,7 +6240,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
@Test // gh-19758
void elvisMiscellaneous() {
SpelParserConfiguration configuration = new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE, null);
SpelParserConfiguration configuration = SpelParserConfiguration.builder().compilerMode(SpelCompilerMode.IMMEDIATE).build();
Expression exp;
exp = new SpelExpressionParser(configuration).parseExpression("bar()");
@@ -6314,7 +6324,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
@Test
void elvis_SPR17214() {
SpelParserConfiguration spc = new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE, null);
SpelParserConfiguration spc = SpelParserConfiguration.builder().compilerMode(SpelCompilerMode.IMMEDIATE).build();
SpelExpressionParser sep = new SpelExpressionParser(spc);
RecordHolder rh = null;
@@ -0,0 +1,247 @@
/*
* Copyright 2002-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
/**
* Tests for {@link SpelParserConfiguration}.
*
* @author Sam Brannen
* @since 7.0.10
*/
class SpelParserConfigurationTests {
@Test
void builderAppliesSameDefaultsAsNoArgConstructor() {
SpelParserConfiguration expected = new SpelParserConfiguration();
SpelParserConfiguration actual = SpelParserConfiguration.builder().build();
assertThat(actual.getCompilerMode()).isEqualTo(expected.getCompilerMode());
assertThat(actual.getCompilerClassLoader()).isEqualTo(expected.getCompilerClassLoader());
assertThat(actual.isAutoGrowNullReferences()).isEqualTo(expected.isAutoGrowNullReferences());
assertThat(actual.isAutoGrowCollections()).isEqualTo(expected.isAutoGrowCollections());
assertThat(actual.getMaximumAutoGrowSize()).isEqualTo(expected.getMaximumAutoGrowSize());
assertThat(actual.getMaximumExpressionLength()).isEqualTo(expected.getMaximumExpressionLength());
assertThat(actual.getMaximumOperations()).isEqualTo(expected.getMaximumOperations());
assertThat(actual.getMaximumBigPowerBits()).isEqualTo(expected.getMaximumBigPowerBits());
assertThat(actual.getMaximumNestingDepth()).isEqualTo(expected.getMaximumNestingDepth());
}
@Test
void withDefaultsMatchesBuilderDefaults() {
SpelParserConfiguration expected = SpelParserConfiguration.builder().build();
SpelParserConfiguration actual = SpelParserConfiguration.withDefaults();
assertThat(actual.getCompilerMode()).isEqualTo(expected.getCompilerMode());
assertThat(actual.getCompilerClassLoader()).isEqualTo(expected.getCompilerClassLoader());
assertThat(actual.isAutoGrowNullReferences()).isEqualTo(expected.isAutoGrowNullReferences());
assertThat(actual.isAutoGrowCollections()).isEqualTo(expected.isAutoGrowCollections());
assertThat(actual.getMaximumAutoGrowSize()).isEqualTo(expected.getMaximumAutoGrowSize());
assertThat(actual.getMaximumExpressionLength()).isEqualTo(expected.getMaximumExpressionLength());
assertThat(actual.getMaximumOperations()).isEqualTo(expected.getMaximumOperations());
assertThat(actual.getMaximumBigPowerBits()).isEqualTo(expected.getMaximumBigPowerBits());
assertThat(actual.getMaximumNestingDepth()).isEqualTo(expected.getMaximumNestingDepth());
}
@Test
void builderAppliesCustomValues() {
ClassLoader classLoader = getClass().getClassLoader();
SpelParserConfiguration configuration = SpelParserConfiguration.builder()
.compilerMode(SpelCompilerMode.IMMEDIATE)
.compilerClassLoader(classLoader)
.autoGrowNullReferences()
.autoGrowCollections()
.maximumAutoGrowSize(99)
.maximumExpressionLength(100)
.maximumOperations(101)
.maximumBigPowerBits(102)
.maximumNestingDepth(103)
.build();
assertThat(configuration.getCompilerMode()).isEqualTo(SpelCompilerMode.IMMEDIATE);
assertThat(configuration.getCompilerClassLoader()).isSameAs(classLoader);
assertThat(configuration.isAutoGrowNullReferences()).isTrue();
assertThat(configuration.isAutoGrowCollections()).isTrue();
assertThat(configuration.getMaximumAutoGrowSize()).isEqualTo(99);
assertThat(configuration.getMaximumExpressionLength()).isEqualTo(100);
assertThat(configuration.getMaximumOperations()).isEqualTo(101);
assertThat(configuration.getMaximumBigPowerBits()).isEqualTo(102);
assertThat(configuration.getMaximumNestingDepth()).isEqualTo(103);
}
@Test
void builderRejectsInvalidValues() {
SpelParserConfiguration.Builder builder = SpelParserConfiguration.builder();
assertThatIllegalArgumentException().isThrownBy(() -> builder.compilerMode(null));
assertThatIllegalArgumentException().isThrownBy(() -> builder.maximumAutoGrowSize(-1));
assertThatIllegalArgumentException().isThrownBy(() -> builder.maximumExpressionLength(0));
assertThatIllegalArgumentException().isThrownBy(() -> builder.maximumOperations(0));
assertThatIllegalArgumentException().isThrownBy(() -> builder.maximumBigPowerBits(0));
assertThatIllegalArgumentException().isThrownBy(() -> builder.maximumNestingDepth(0));
}
/**
* Regression tests for the legacy constructors in {@link SpelParserConfiguration}.
* <p>Elsewhere in the test suite, we prefer {@link SpelParserConfiguration#builder()}
* over these constructors. Thus, this nested class exists so that constructor coverage
* remains in one place, which will also keep any future deprecation warnings for
* these constructors confined to this class.
*/
@Nested
class LegacyConstructorTests {
@Test
void noArgConstructorAppliesDefaults() {
SpelParserConfiguration configuration = new SpelParserConfiguration();
assertThat(configuration.getCompilerMode()).isEqualTo(SpelCompilerMode.OFF);
assertThat(configuration.getCompilerClassLoader()).isNull();
assertThat(configuration.isAutoGrowNullReferences()).isFalse();
assertThat(configuration.isAutoGrowCollections()).isFalse();
assertThat(configuration.getMaximumAutoGrowSize())
.isEqualTo(SpelParserConfiguration.DEFAULT_MAX_AUTO_GROW_SIZE);
assertThat(configuration.getMaximumExpressionLength())
.isEqualTo(SpelParserConfiguration.DEFAULT_MAX_EXPRESSION_LENGTH);
assertThat(configuration.getMaximumOperations())
.isEqualTo(SpelParserConfiguration.DEFAULT_MAX_OPERATIONS);
assertThat(configuration.getMaximumBigPowerBits())
.isEqualTo(SpelParserConfiguration.DEFAULT_MAX_BIG_POWER_BITS);
}
@Test
void compilerModeAndClassLoaderConstructor() {
ClassLoader classLoader = getClass().getClassLoader();
SpelParserConfiguration configuration =
new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE, classLoader);
assertThat(configuration.getCompilerMode()).isEqualTo(SpelCompilerMode.IMMEDIATE);
assertThat(configuration.getCompilerClassLoader()).isSameAs(classLoader);
assertThat(configuration.isAutoGrowNullReferences()).isFalse();
assertThat(configuration.isAutoGrowCollections()).isFalse();
assertThat(configuration.getMaximumAutoGrowSize())
.isEqualTo(SpelParserConfiguration.DEFAULT_MAX_AUTO_GROW_SIZE);
}
@Test
void autoGrowFlagsConstructor() {
SpelParserConfiguration configuration = new SpelParserConfiguration(true, true);
assertThat(configuration.getCompilerMode()).isEqualTo(SpelCompilerMode.OFF);
assertThat(configuration.isAutoGrowNullReferences()).isTrue();
assertThat(configuration.isAutoGrowCollections()).isTrue();
assertThat(configuration.getMaximumAutoGrowSize())
.isEqualTo(SpelParserConfiguration.DEFAULT_MAX_AUTO_GROW_SIZE);
}
@Test
void autoGrowFlagsAndMaximumAutoGrowSizeConstructor() {
SpelParserConfiguration configuration = new SpelParserConfiguration(true, true, 99);
assertThat(configuration.isAutoGrowNullReferences()).isTrue();
assertThat(configuration.isAutoGrowCollections()).isTrue();
assertThat(configuration.getMaximumAutoGrowSize()).isEqualTo(99);
}
@Test
void fiveArgConstructorAppliesAllValues() {
ClassLoader classLoader = getClass().getClassLoader();
SpelParserConfiguration configuration = new SpelParserConfiguration(
SpelCompilerMode.IMMEDIATE, classLoader, true, true, 99);
assertThat(configuration.getCompilerMode()).isEqualTo(SpelCompilerMode.IMMEDIATE);
assertThat(configuration.getCompilerClassLoader()).isSameAs(classLoader);
assertThat(configuration.isAutoGrowNullReferences()).isTrue();
assertThat(configuration.isAutoGrowCollections()).isTrue();
assertThat(configuration.getMaximumAutoGrowSize()).isEqualTo(99);
assertThat(configuration.getMaximumExpressionLength())
.isEqualTo(SpelParserConfiguration.DEFAULT_MAX_EXPRESSION_LENGTH);
}
@Test
void sixArgConstructorAppliesMaximumExpressionLength() {
SpelParserConfiguration configuration = new SpelParserConfiguration(
SpelCompilerMode.IMMEDIATE, null, true, true, 99, 100);
assertThat(configuration.getMaximumExpressionLength()).isEqualTo(100);
assertThat(configuration.getMaximumOperations())
.isEqualTo(SpelParserConfiguration.DEFAULT_MAX_OPERATIONS);
}
@Test
void sevenArgConstructorAppliesMaximumOperations() {
SpelParserConfiguration configuration = new SpelParserConfiguration(
SpelCompilerMode.IMMEDIATE, null, true, true, 99, 100, 101);
assertThat(configuration.getMaximumOperations()).isEqualTo(101);
assertThat(configuration.getMaximumBigPowerBits())
.isEqualTo(SpelParserConfiguration.DEFAULT_MAX_BIG_POWER_BITS);
}
@Test
void eightArgConstructorAppliesMaximumBigPowerBits() {
SpelParserConfiguration configuration = new SpelParserConfiguration(
SpelCompilerMode.IMMEDIATE, null, true, true, 99, 100, 101, 102);
assertThat(configuration.getMaximumBigPowerBits()).isEqualTo(102);
assertThat(configuration.getMaximumNestingDepth())
.isEqualTo(SpelParserConfiguration.DEFAULT_MAX_EXPRESSION_NESTING_DEPTH);
}
@Test
void canonicalConstructorAppliesAllValues() {
ClassLoader classLoader = getClass().getClassLoader();
SpelParserConfiguration configuration = new SpelParserConfiguration(
SpelCompilerMode.IMMEDIATE, classLoader, true, true, 99, 100, 101, 102, 103);
assertThat(configuration.getCompilerMode()).isEqualTo(SpelCompilerMode.IMMEDIATE);
assertThat(configuration.getCompilerClassLoader()).isSameAs(classLoader);
assertThat(configuration.isAutoGrowNullReferences()).isTrue();
assertThat(configuration.isAutoGrowCollections()).isTrue();
assertThat(configuration.getMaximumAutoGrowSize()).isEqualTo(99);
assertThat(configuration.getMaximumExpressionLength()).isEqualTo(100);
assertThat(configuration.getMaximumOperations()).isEqualTo(101);
assertThat(configuration.getMaximumBigPowerBits()).isEqualTo(102);
assertThat(configuration.getMaximumNestingDepth()).isEqualTo(103);
}
@Test
void canonicalConstructorRejectsInvalidValues() {
assertThatIllegalArgumentException().isThrownBy(() ->
new SpelParserConfiguration(null, null, false, false, 0, 1, 1, 1, 1));
assertThatIllegalArgumentException().isThrownBy(() ->
new SpelParserConfiguration(SpelCompilerMode.OFF, null, false, false, -1, 1, 1, 1, 1));
assertThatIllegalArgumentException().isThrownBy(() ->
new SpelParserConfiguration(SpelCompilerMode.OFF, null, false, false, 0, 0, 1, 1, 1));
assertThatIllegalArgumentException().isThrownBy(() ->
new SpelParserConfiguration(SpelCompilerMode.OFF, null, false, false, 0, 1, 0, 1, 1));
assertThatIllegalArgumentException().isThrownBy(() ->
new SpelParserConfiguration(SpelCompilerMode.OFF, null, false, false, 0, 1, 1, 0, 1));
assertThatIllegalArgumentException().isThrownBy(() ->
new SpelParserConfiguration(SpelCompilerMode.OFF, null, false, false, 0, 1, 1, 1, 0));
}
}
}
@@ -696,8 +696,8 @@ class SpelReproTests extends AbstractExpressionTests {
@Test
void compiledExpressionForProxy_SPR16191() {
SpelExpressionParser expressionParser =
new SpelExpressionParser(new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE, null));
SpelExpressionParser expressionParser = new SpelExpressionParser(
SpelParserConfiguration.builder().compilerMode(SpelCompilerMode.IMMEDIATE).build());
Expression expression = expressionParser.parseExpression("#target.process(#root)");
VarargsReceiver receiver = new VarargsReceiver();
@@ -1274,7 +1274,7 @@ class SpelReproTests extends AbstractExpressionTests {
@Test
void SPR10452() {
SpelParserConfiguration configuration = new SpelParserConfiguration(false, false);
SpelParserConfiguration configuration = SpelParserConfiguration.withDefaults();
ExpressionParser parser = new SpelExpressionParser(configuration);
StandardEvaluationContext context = new StandardEvaluationContext();
@@ -1299,7 +1299,7 @@ class SpelReproTests extends AbstractExpressionTests {
@Test
void SPR9495() {
SpelParserConfiguration configuration = new SpelParserConfiguration(false, false);
SpelParserConfiguration configuration = SpelParserConfiguration.withDefaults();
ExpressionParser parser = new SpelExpressionParser(configuration);
StandardEvaluationContext context = new StandardEvaluationContext();
@@ -46,7 +46,7 @@ class SpelCompilerTests {
@Test // gh-24357
void expressionCompilesWhenMethodComesFromPublicInterface() {
SpelParserConfiguration config = new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE, null);
SpelParserConfiguration config = SpelParserConfiguration.builder().compilerMode(SpelCompilerMode.IMMEDIATE).build();
SpelExpressionParser parser = new SpelExpressionParser(config);
OrderedComponent component = new OrderedComponent();
@@ -59,7 +59,7 @@ class SpelCompilerTests {
@Test // gh-25706
void defaultMethodInvocation() {
SpelParserConfiguration config = new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE, null);
SpelParserConfiguration config = SpelParserConfiguration.builder().compilerMode(SpelCompilerMode.IMMEDIATE).build();
SpelExpressionParser parser = new SpelExpressionParser(config);
StandardEvaluationContext context = new StandardEvaluationContext();
@@ -84,7 +84,7 @@ class SpelCompilerTests {
@Test
void simpleEvaluationContextBlocksCompilationByDefault() {
SpelParserConfiguration config = new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE, null);
SpelParserConfiguration config = SpelParserConfiguration.builder().compilerMode(SpelCompilerMode.IMMEDIATE).build();
SpelExpressionParser parser = new SpelExpressionParser(config);
// "order" resides in the public Ordered interface and is therefore compilable,
// so any non-compilation is attributable solely to the context's policy.
@@ -102,7 +102,7 @@ class SpelCompilerTests {
@Test
void simpleEvaluationContextAllowsCompilationWhenSupported() {
SpelParserConfiguration config = new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE, null);
SpelParserConfiguration config = SpelParserConfiguration.builder().compilerMode(SpelCompilerMode.IMMEDIATE).build();
SpelExpressionParser parser = new SpelExpressionParser(config);
// "order" resides in the public Ordered interface and is therefore compilable.
Expression expression = parser.parseExpression("order");
@@ -120,7 +120,7 @@ class SpelCompilerTests {
@Test
void simpleEvaluationContextIgnoresPrecompiledExpressionByDefault() {
SpelParserConfiguration config = new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE, null);
SpelParserConfiguration config = SpelParserConfiguration.builder().compilerMode(SpelCompilerMode.IMMEDIATE).build();
SpelExpressionParser parser = new SpelExpressionParser(config);
// "order" resides in the public Ordered interface and is therefore compilable.
Expression expression = parser.parseExpression("order");
@@ -155,7 +155,7 @@ class SpelCompilerTests {
*/
@Test
void simpleEvaluationContextSetAsDefaultBlocksCompilationForImplicitContextVariants() {
SpelParserConfiguration config = new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE, null);
SpelParserConfiguration config = SpelParserConfiguration.builder().compilerMode(SpelCompilerMode.IMMEDIATE).build();
SpelExpressionParser parser = new SpelExpressionParser(config);
// "order" resides in the public Ordered interface and is therefore compilable,
// so any non-compilation is attributable solely to the context's policy.
@@ -193,7 +193,7 @@ class SpelCompilerTests {
*/
@Test
void simpleEvaluationContextSetAsDefaultIgnoresPrecompiledExpressionForImplicitContextVariants() {
SpelParserConfiguration config = new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE, null);
SpelParserConfiguration config = SpelParserConfiguration.builder().compilerMode(SpelCompilerMode.IMMEDIATE).build();
SpelExpressionParser parser = new SpelExpressionParser(config);
// "order" resides in the public Ordered interface and is therefore compilable.
SpelExpression expression = parser.parseRaw("order");
@@ -240,7 +240,7 @@ class SpelCompilerTests {
@Test // gh-28043
void changingRegisteredVariableTypeDoesNotResultInFailureInMixedMode() {
SpelParserConfiguration config = new SpelParserConfiguration(SpelCompilerMode.MIXED, null);
SpelParserConfiguration config = SpelParserConfiguration.builder().compilerMode(SpelCompilerMode.MIXED).build();
SpelExpressionParser parser = new SpelExpressionParser(config);
Expression sharedExpression = parser.parseExpression("#bean.value");
StandardEvaluationContext context = new StandardEvaluationContext();