mirror of
https://github.com/spring-projects/spring-framework.git
synced 2026-09-17 16:39:29 +00:00
Introduce a builder for SpelParserConfiguration
Prior to this commit, SpelParserConfiguration exposed 8 overloaded constructors that accumulated over time as new configuration options were introduced (auto-grow support since 3.0, maximumExpressionLength in 5.2.25, maximumOperations in 6.2.19, and maximumBigPowerBits in 7.0.9), culminating in an 8-parameter constructor. This made call sites hard to read due to unlabeled sequences of booleans and ints, and it forced users who wanted to override a single setting to also supply every other value explicitly. To address that, this commit introduces a builder API in SpelParserConfiguration, following the pattern already established by SimpleEvaluationContext's builder API. Specifically, SpelParserConfiguration.builder() returns a Builder that is pre-populated with the same defaults as the no-arg constructor, including the SpringProperties-driven overrides for the default compiler mode, maximum operations, and maximum big-power bits -- the latter two are only resolved lazily in build(), so that overriding them via the builder never triggers an unnecessary SpringProperties lookup. Each property has a dedicated, named setter (compilerMode(), compilerClassLoader(), maximumAutoGrowSize(), maximumExpressionLength(), maximumOperations(), maximumBigPowerBits()), and the two auto-grow flags are exposed as simple no-arg opt-ins (autoGrowNullReferences(), autoGrowCollections()) since they both default to false. build() delegates to the existing canonical constructor, so validation and defaults remain centralized in one place. In addition, a new SpelParserConfiguration.withDefaults() factory method has been introduced as shorthand for SpelParserConfiguration.builder().build(), for the common case where none of the builder's defaults need to be overridden. As the one deliberate exception to matching the no-arg constructor's defaults, the builder defaults maximumAutoGrowSize to 256 -- aligned with DataBinder.DEFAULT_AUTO_GROW_COLLECTION_LIMIT -- rather than the constructors' Integer.MAX_VALUE. The constructors keep their legacy default for backward compatibility, but the builder is a new, opt-in API that is not bound by that compatibility contract. This change is purely additive: none of the existing constructors have been modified or deprecated. Deprecating those constructors in favor of the builder is being deferred to 7.1, since new deprecations should not be introduced in a patch release. In the meantime, the Javadoc for the constructors and for the SPRING_EXPRESSION_*_PROPERTY_NAME constants has been updated to favor the builder (or a specific Builder setter) instead of the constructors, and the class-level Javadoc now states that the constructors are planned to be deprecated in favor of the builder as of Spring Framework 7.1. SpelExpressionParser's no-arg constructor, ExpressionState's two convenience constructors, and StandardBeanExpressionResolver's ClassLoader-based constructor have all been switched from the SpelParserConfiguration constructors to the builder (or withDefaults()). This is behaviorally identical in every case: autoGrowCollections remains false at each of those call sites, and maximumAutoGrowSize -- the only property whose default differs between the constructors and the builder -- has no effect when autoGrowCollections is false. Tests have been added in a new SpelParserConfigurationTests class to verify that the builder's defaults match the no-arg constructor (with the one intentional maximumAutoGrowSize exception called out above), that custom values are applied correctly, and that invalid values are rejected. The nested LegacyConstructorTests class provides regression coverage for each of the legacy constructors, consolidating their usage in tests to a single class -- which will keep any future deprecation warnings confined to this class -- and documents that, unlike the builder, the canonical constructor does not (yet) reject a negative maximumAutoGrowSize. The remaining incidental usages of the SpelParserConfiguration constructors throughout EvaluationTests, IndexingTests, SpelCompilationCoverageTests, SpelReproTests, and SpelCompilerTests have been converted to use the builder. Furthermore, the reference documentation has been updated to recommend the builder and withDefaults() over the constructors, both in prose and in the Java/Kotlin examples. Closes gh-37187
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -556,7 +562,8 @@ Kotlin::
|
||||
|
||||
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
|
||||
@@ -567,12 +574,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
|
||||
@@ -580,13 +589,14 @@ 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]).
|
||||
|
||||
[[expressions-spel-compilation]]
|
||||
== SpEL Compilation
|
||||
@@ -669,8 +679,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);
|
||||
|
||||
@@ -685,8 +697,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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user