Revise SpEL Evaluation documentation
This commit is contained in:
parent
4d11307b84
commit
b823c46aae
|
@ -1,12 +1,12 @@
|
||||||
[[expressions-evaluation]]
|
[[expressions-evaluation]]
|
||||||
= Evaluation
|
= Evaluation
|
||||||
|
|
||||||
This section introduces the simple use of SpEL interfaces and its expression language.
|
This section introduces programmatic use of SpEL's interfaces and its expression language.
|
||||||
The complete language reference can be found in
|
The complete language reference can be found in the
|
||||||
xref:core/expressions/language-ref.adoc[Language Reference].
|
xref:core/expressions/language-ref.adoc[Language Reference].
|
||||||
|
|
||||||
The following code introduces the SpEL API to evaluate the literal string expression,
|
The following code demonstrates how to use the SpEL API to evaluate the literal string
|
||||||
`Hello World`.
|
expression, `Hello World`.
|
||||||
|
|
||||||
[tabs]
|
[tabs]
|
||||||
======
|
======
|
||||||
|
@ -31,21 +31,21 @@ Kotlin::
|
||||||
<1> The value of the message variable is `'Hello World'`.
|
<1> The value of the message variable is `'Hello World'`.
|
||||||
======
|
======
|
||||||
|
|
||||||
|
|
||||||
The SpEL classes and interfaces you are most likely to use are located in the
|
The SpEL classes and interfaces you are most likely to use are located in the
|
||||||
`org.springframework.expression` package and its sub-packages, such as `spel.support`.
|
`org.springframework.expression` package and its sub-packages, such as `spel.support`.
|
||||||
|
|
||||||
The `ExpressionParser` interface is responsible for parsing an expression string. In
|
The `ExpressionParser` interface is responsible for parsing an expression string. In the
|
||||||
the preceding example, the expression string is a string literal denoted by the surrounding single
|
preceding example, the expression string is a string literal denoted by the surrounding
|
||||||
quotation marks. The `Expression` interface is responsible for evaluating the previously defined
|
single quotation marks. The `Expression` interface is responsible for evaluating the
|
||||||
expression string. Two exceptions that can be thrown, `ParseException` and
|
defined expression string. The two types of exceptions that can be thrown when calling
|
||||||
`EvaluationException`, when calling `parser.parseExpression` and `exp.getValue`,
|
`parser.parseExpression(...)` and `exp.getValue(...)` are `ParseException` and
|
||||||
respectively.
|
`EvaluationException`, respectively.
|
||||||
|
|
||||||
SpEL supports a wide range of features, such as calling methods, accessing properties,
|
SpEL supports a wide range of features such as calling methods, accessing properties,
|
||||||
and calling constructors.
|
and calling constructors.
|
||||||
|
|
||||||
In the following example of method invocation, we call the `concat` method on the string literal:
|
In the following method invocation example, we call the `concat` method on the string
|
||||||
|
literal, `Hello World`.
|
||||||
|
|
||||||
[tabs]
|
[tabs]
|
||||||
======
|
======
|
||||||
|
@ -70,7 +70,8 @@ Kotlin::
|
||||||
<1> The value of `message` is now 'Hello World!'.
|
<1> The value of `message` is now 'Hello World!'.
|
||||||
======
|
======
|
||||||
|
|
||||||
The following example of calling a JavaBean property calls the `String` property `Bytes`:
|
The following example demonstrates how to access the `Bytes` JavaBean property of the
|
||||||
|
string literal, `Hello World`.
|
||||||
|
|
||||||
[tabs]
|
[tabs]
|
||||||
======
|
======
|
||||||
|
@ -100,10 +101,10 @@ Kotlin::
|
||||||
======
|
======
|
||||||
|
|
||||||
SpEL also supports nested properties by using the standard dot notation (such as
|
SpEL also supports nested properties by using the standard dot notation (such as
|
||||||
`prop1.prop2.prop3`) and also the corresponding setting of property values.
|
`prop1.prop2.prop3`) as well as the corresponding setting of property values.
|
||||||
Public fields may also be accessed.
|
Public fields may also be accessed.
|
||||||
|
|
||||||
The following example shows how to use dot notation to get the length of a literal:
|
The following example shows how to use dot notation to get the length of a string literal.
|
||||||
|
|
||||||
[tabs]
|
[tabs]
|
||||||
======
|
======
|
||||||
|
@ -133,7 +134,7 @@ Kotlin::
|
||||||
======
|
======
|
||||||
|
|
||||||
The String's constructor can be called instead of using a string literal, as the following
|
The String's constructor can be called instead of using a string literal, as the following
|
||||||
example shows:
|
example shows.
|
||||||
|
|
||||||
[tabs]
|
[tabs]
|
||||||
======
|
======
|
||||||
|
@ -145,7 +146,7 @@ Java::
|
||||||
Expression exp = parser.parseExpression("new String('hello world').toUpperCase()"); // <1>
|
Expression exp = parser.parseExpression("new String('hello world').toUpperCase()"); // <1>
|
||||||
String message = exp.getValue(String.class);
|
String message = exp.getValue(String.class);
|
||||||
----
|
----
|
||||||
<1> Construct a new `String` from the literal and make it be upper case.
|
<1> Construct a new `String` from the literal and convert it to upper case.
|
||||||
|
|
||||||
Kotlin::
|
Kotlin::
|
||||||
+
|
+
|
||||||
|
@ -155,10 +156,9 @@ Kotlin::
|
||||||
val exp = parser.parseExpression("new String('hello world').toUpperCase()") // <1>
|
val exp = parser.parseExpression("new String('hello world').toUpperCase()") // <1>
|
||||||
val message = exp.getValue(String::class.java)
|
val message = exp.getValue(String::class.java)
|
||||||
----
|
----
|
||||||
<1> Construct a new `String` from the literal and make it be upper case.
|
<1> Construct a new `String` from the literal and convert it to upper case.
|
||||||
======
|
======
|
||||||
|
|
||||||
|
|
||||||
Note the use of the generic method: `public <T> T getValue(Class<T> desiredResultType)`.
|
Note the use of the generic method: `public <T> T getValue(Class<T> desiredResultType)`.
|
||||||
Using this method removes the need to cast the value of the expression to the desired
|
Using this method removes the need to cast the value of the expression to the desired
|
||||||
result type. An `EvaluationException` is thrown if the value cannot be cast to the
|
result type. An `EvaluationException` is thrown if the value cannot be cast to the
|
||||||
|
@ -166,8 +166,8 @@ type `T` or converted by using the registered type converter.
|
||||||
|
|
||||||
The more common usage of SpEL is to provide an expression string that is evaluated
|
The more common usage of SpEL is to provide an expression string that is evaluated
|
||||||
against a specific object instance (called the root object). The following example shows
|
against a specific object instance (called the root object). The following example shows
|
||||||
how to retrieve the `name` property from an instance of the `Inventor` class or
|
how to retrieve the `name` property from an instance of the `Inventor` class and how to
|
||||||
create a boolean condition:
|
reference the `name` property in a boolean expression.
|
||||||
|
|
||||||
[tabs]
|
[tabs]
|
||||||
======
|
======
|
||||||
|
@ -240,7 +240,7 @@ It excludes Java type references, constructors, and bean references. It also req
|
||||||
you to explicitly choose the level of support for properties and methods in expressions.
|
you to explicitly choose the level of support for properties and methods in expressions.
|
||||||
By default, the `create()` static factory method enables only read access to properties.
|
By default, the `create()` static factory method enables only read access to properties.
|
||||||
You can also obtain a builder to configure the exact level of support needed, targeting
|
You can also obtain a builder to configure the exact level of support needed, targeting
|
||||||
one or some combination of the following:
|
one or some combination of the following.
|
||||||
|
|
||||||
* Custom `PropertyAccessor` only (no reflection)
|
* Custom `PropertyAccessor` only (no reflection)
|
||||||
* Data binding properties for read-only access
|
* Data binding properties for read-only access
|
||||||
|
@ -252,16 +252,15 @@ one or some combination of the following:
|
||||||
|
|
||||||
By default, SpEL uses the conversion service available in Spring core
|
By default, SpEL uses the conversion service available in Spring core
|
||||||
(`org.springframework.core.convert.ConversionService`). This conversion service comes
|
(`org.springframework.core.convert.ConversionService`). This conversion service comes
|
||||||
with many built-in converters for common conversions but is also fully extensible so that
|
with many built-in converters for common conversions, but is also fully extensible so
|
||||||
you can add custom conversions between types. Additionally, it is
|
that you can add custom conversions between types. Additionally, it is generics-aware.
|
||||||
generics-aware. This means that, when you work with generic types in
|
This means that, when you work with generic types in expressions, SpEL attempts
|
||||||
expressions, SpEL attempts conversions to maintain type correctness for any objects
|
conversions to maintain type correctness for any objects it encounters.
|
||||||
it encounters.
|
|
||||||
|
|
||||||
What does this mean in practice? Suppose assignment, using `setValue()`, is being used
|
What does this mean in practice? Suppose assignment, using `setValue()`, is being used
|
||||||
to set a `List` property. The type of the property is actually `List<Boolean>`. SpEL
|
to set a `List` property. The type of the property is actually `List<Boolean>`. SpEL
|
||||||
recognizes that the elements of the list need to be converted to `Boolean` before
|
recognizes that the elements of the list need to be converted to `Boolean` before
|
||||||
being placed in it. The following example shows how to do so:
|
being placed in it. The following example shows how to do so.
|
||||||
|
|
||||||
[tabs]
|
[tabs]
|
||||||
======
|
======
|
||||||
|
@ -325,7 +324,7 @@ constructor before setting the specified value. If the element type does not hav
|
||||||
default constructor, `null` will be added to the array or list. If there is no built-in
|
default constructor, `null` will be added to the array or list. If there is no built-in
|
||||||
or custom converter that knows how to set the value, `null` will remain in the array or
|
or custom converter that knows how to set the value, `null` will remain in the array or
|
||||||
list at the specified index. The following example demonstrates how to automatically grow
|
list at the specified index. The following example demonstrates how to automatically grow
|
||||||
the list:
|
the list.
|
||||||
|
|
||||||
[tabs]
|
[tabs]
|
||||||
======
|
======
|
||||||
|
@ -394,11 +393,11 @@ xref:appendix.adoc#appendix-spring-properties[Supported Spring Properties]).
|
||||||
[[expressions-spel-compilation]]
|
[[expressions-spel-compilation]]
|
||||||
== SpEL Compilation
|
== SpEL Compilation
|
||||||
|
|
||||||
Spring Framework 4.1 includes a basic expression compiler. Expressions are usually
|
Spring provides a basic compiler for SpEL expressions. Expressions are usually
|
||||||
interpreted, which provides a lot of dynamic flexibility during evaluation but
|
interpreted, which provides a lot of dynamic flexibility during evaluation but does not
|
||||||
does not provide optimum performance. For occasional expression usage,
|
provide optimum performance. For occasional expression usage, this is fine, but, when
|
||||||
this is fine, but, when used by other components such as Spring Integration,
|
used by other components such as Spring Integration, performance can be very important,
|
||||||
performance can be very important, and there is no real need for the dynamism.
|
and there is no real need for the dynamism.
|
||||||
|
|
||||||
The SpEL compiler is intended to address this need. During evaluation, the compiler
|
The SpEL compiler is intended to address this need. During evaluation, the compiler
|
||||||
generates a Java class that embodies the expression behavior at runtime and uses that
|
generates a Java class that embodies the expression behavior at runtime and uses that
|
||||||
|
@ -411,16 +410,17 @@ information can cause trouble later if the types of the various expression eleme
|
||||||
change over time. For this reason, compilation is best suited to expressions whose
|
change over time. For this reason, compilation is best suited to expressions whose
|
||||||
type information is not going to change on repeated evaluations.
|
type information is not going to change on repeated evaluations.
|
||||||
|
|
||||||
Consider the following basic expression:
|
Consider the following basic expression.
|
||||||
|
|
||||||
|
[source,java,indent=0,subs="verbatim,quotes"]
|
||||||
----
|
----
|
||||||
someArray[0].someProperty.someOtherProperty < 0.1
|
someArray[0].someProperty.someOtherProperty < 0.1
|
||||||
----
|
----
|
||||||
|
|
||||||
Because the preceding expression involves array access, some property de-referencing,
|
Because the preceding expression involves array access, some property de-referencing, and
|
||||||
and numeric operations, the performance gain can be very noticeable. In an example
|
numeric operations, the performance gain can be very noticeable. In an example micro
|
||||||
micro benchmark run of 50000 iterations, it took 75ms to evaluate by using the
|
benchmark run of 50,000 iterations, it took 75ms to evaluate by using the interpreter and
|
||||||
interpreter and only 3ms using the compiled version of the expression.
|
only 3ms using the compiled version of the expression.
|
||||||
|
|
||||||
|
|
||||||
[[expressions-compiler-configuration]]
|
[[expressions-compiler-configuration]]
|
||||||
|
@ -428,33 +428,34 @@ interpreter and only 3ms using the compiled version of the expression.
|
||||||
|
|
||||||
The compiler is not turned on by default, but you can turn it on in either of two
|
The compiler is not turned on by default, but you can turn it on in either of two
|
||||||
different ways. You can turn it on by using the parser configuration process
|
different ways. You can turn it on by using the parser configuration process
|
||||||
(xref:core/expressions/evaluation.adoc#expressions-parser-configuration[discussed earlier]) or by using a Spring property
|
(xref:core/expressions/evaluation.adoc#expressions-parser-configuration[discussed
|
||||||
when SpEL usage is embedded inside another component. This section discusses both of
|
earlier]) or by using a Spring property when SpEL usage is embedded inside another
|
||||||
these options.
|
component. This section discusses both of these options.
|
||||||
|
|
||||||
The compiler can operate in one of three modes, which are captured in the
|
The compiler can operate in one of three modes, which are captured in the
|
||||||
`org.springframework.expression.spel.SpelCompilerMode` enum. The modes are as follows:
|
`org.springframework.expression.spel.SpelCompilerMode` enum. The modes are as follows.
|
||||||
|
|
||||||
* `OFF` (default): The compiler is switched off.
|
* `OFF` (default): The compiler is switched off.
|
||||||
* `IMMEDIATE`: In immediate mode, the expressions are compiled as soon as possible. This
|
* `IMMEDIATE`: In immediate mode, the expressions are compiled as soon as possible. This
|
||||||
is typically after the first interpreted evaluation. If the compiled expression fails
|
is typically after the first interpreted evaluation. If the compiled expression fails
|
||||||
(typically due to a type changing, as described earlier), the caller of the expression
|
(typically due to a type changing, as described earlier), the caller of the expression
|
||||||
evaluation receives an exception.
|
evaluation receives an exception.
|
||||||
* `MIXED`: In mixed mode, the expressions silently switch between interpreted and compiled
|
* `MIXED`: In mixed mode, the expressions silently switch between interpreted and
|
||||||
mode over time. After some number of interpreted runs, they switch to compiled
|
compiled mode over time. After some number of interpreted runs, they switch to compiled
|
||||||
form and, if something goes wrong with the compiled form (such as a type changing, as
|
form and, if something goes wrong with the compiled form (such as a type changing, as
|
||||||
described earlier), the expression automatically switches back to interpreted form
|
described earlier), the expression automatically switches back to interpreted form
|
||||||
again. Sometime later, it may generate another compiled form and switch to it. Basically,
|
again. Sometime later, it may generate another compiled form and switch to it.
|
||||||
the exception that the user gets in `IMMEDIATE` mode is instead handled internally.
|
Basically, the exception that the user gets in `IMMEDIATE` mode is instead handled
|
||||||
|
internally.
|
||||||
|
|
||||||
`IMMEDIATE` mode exists because `MIXED` mode could cause issues for expressions that
|
`IMMEDIATE` mode exists because `MIXED` mode could cause issues for expressions that
|
||||||
have side effects. If a compiled expression blows up after partially succeeding, it
|
have side effects. If a compiled expression blows up after partially succeeding, it
|
||||||
may have already done something that has affected the state of the system. If this
|
may have already done something that has affected the state of the system. If this
|
||||||
has happened, the caller may not want it to silently re-run in interpreted mode,
|
has happened, the caller may not want it to silently re-run in interpreted mode,
|
||||||
since part of the expression may be running twice.
|
since part of the expression may be run twice.
|
||||||
|
|
||||||
After selecting a mode, use the `SpelParserConfiguration` to configure the parser. The
|
After selecting a mode, use the `SpelParserConfiguration` to configure the parser. The
|
||||||
following example shows how to do so:
|
following example shows how to do so.
|
||||||
|
|
||||||
[tabs]
|
[tabs]
|
||||||
======
|
======
|
||||||
|
@ -491,15 +492,16 @@ Kotlin::
|
||||||
----
|
----
|
||||||
======
|
======
|
||||||
|
|
||||||
When you specify the compiler mode, you can also specify a classloader (passing null is allowed).
|
When you specify the compiler mode, you can also specify a `ClassLoader` (passing `null`
|
||||||
Compiled expressions are defined in a child classloader created under any that is supplied.
|
is allowed). Compiled expressions are defined in a child `ClassLoader` created under any
|
||||||
It is important to ensure that, if a classloader is specified, it can see all the types involved in
|
that is supplied. It is important to ensure that, if a `ClassLoader` is specified, it can
|
||||||
the expression evaluation process. If you do not specify a classloader, a default classloader is used
|
see all the types involved in the expression evaluation process. If you do not specify a
|
||||||
(typically the context classloader for the thread that is running during expression evaluation).
|
`ClassLoader`, a default `ClassLoader` is used (typically the context `ClassLoader` for
|
||||||
|
the thread that is running during expression evaluation).
|
||||||
|
|
||||||
The second way to configure the compiler is for use when SpEL is embedded inside some
|
The second way to configure the compiler is for use when SpEL is embedded inside some
|
||||||
other component and it may not be possible to configure it through a configuration
|
other component and it may not be possible to configure it through a configuration
|
||||||
object. In these cases, it is possible to set the `spring.expression.compiler.mode`
|
object. In such cases, it is possible to set the `spring.expression.compiler.mode`
|
||||||
property via a JVM system property (or via the
|
property via a JVM system property (or via the
|
||||||
xref:appendix.adoc#appendix-spring-properties[`SpringProperties`] mechanism) to one of the
|
xref:appendix.adoc#appendix-spring-properties[`SpringProperties`] mechanism) to one of the
|
||||||
`SpelCompilerMode` enum values (`off`, `immediate`, or `mixed`).
|
`SpelCompilerMode` enum values (`off`, `immediate`, or `mixed`).
|
||||||
|
@ -508,18 +510,14 @@ xref:appendix.adoc#appendix-spring-properties[`SpringProperties`] mechanism) to
|
||||||
[[expressions-compiler-limitations]]
|
[[expressions-compiler-limitations]]
|
||||||
=== Compiler Limitations
|
=== Compiler Limitations
|
||||||
|
|
||||||
Since Spring Framework 4.1, the basic compilation framework is in place. However, the framework
|
Spring does not support compiling every kind of expression. The primary focus is on
|
||||||
does not yet support compiling every kind of expression. The initial focus has been on the
|
common expressions that are likely to be used in performance-critical contexts. The
|
||||||
common expressions that are likely to be used in performance-critical contexts. The following
|
following kinds of expressions cannot be compiled.
|
||||||
kinds of expression cannot be compiled at the moment:
|
|
||||||
|
|
||||||
* Expressions involving assignment
|
* Expressions involving assignment
|
||||||
* Expressions relying on the conversion service
|
* Expressions relying on the conversion service
|
||||||
* Expressions using custom resolvers or accessors
|
* Expressions using custom resolvers or accessors
|
||||||
* Expressions using selection or projection
|
* Expressions using selection or projection
|
||||||
|
|
||||||
More types of expressions will be compilable in the future.
|
Compilation of additional kinds of expressions may be supported in the future.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue