|
|
|
@ -7,17 +7,17 @@ |
|
|
|
[[expressions-intro]] |
|
|
|
[[expressions-intro]] |
|
|
|
== Introduction |
|
|
|
== Introduction |
|
|
|
|
|
|
|
|
|
|
|
The Spring Expression Language (SpEL for short) is a powerful expression language that |
|
|
|
The Spring Expression Language (_SpEL_ for short) is a powerful expression language that |
|
|
|
supports querying and manipulating an object graph at runtime. The language syntax is |
|
|
|
supports querying and manipulating an object graph at runtime. The language syntax is |
|
|
|
similar to Unified EL but offers additional features, most notably method invocation and |
|
|
|
similar to Unified EL but offers additional features, most notably method invocation and |
|
|
|
basic string templating functionality. |
|
|
|
basic string templating functionality. |
|
|
|
|
|
|
|
|
|
|
|
While there are several other Java expression languages available, OGNL, MVEL, and JBoss |
|
|
|
While there are several other Java expression languages available -- OGNL, MVEL, and JBoss |
|
|
|
EL, to name a few, the Spring Expression Language was created to provide the Spring |
|
|
|
EL, to name a few -- the Spring Expression Language was created to provide the Spring |
|
|
|
community with a single well supported expression language that can be used across all |
|
|
|
community with a single well supported expression language that can be used across all |
|
|
|
the products in the Spring portfolio. Its language features are driven by the |
|
|
|
the products in the Spring portfolio. Its language features are driven by the |
|
|
|
requirements of the projects in the Spring portfolio, including tooling requirements for |
|
|
|
requirements of the projects in the Spring portfolio, including tooling requirements for |
|
|
|
code completion support within the eclipse based Spring Tool Suite. That said, |
|
|
|
code completion support within the Eclipse based Spring Tool Suite. That said, |
|
|
|
SpEL is based on a technology agnostic API allowing other expression language |
|
|
|
SpEL is based on a technology agnostic API allowing other expression language |
|
|
|
implementations to be integrated should the need arise. |
|
|
|
implementations to be integrated should the need arise. |
|
|
|
|
|
|
|
|
|
|
|
@ -29,10 +29,10 @@ infrastructure classes such as the parser. Most Spring users will not need to de |
|
|
|
this infrastructure and will instead only author expression strings for evaluation. An |
|
|
|
this infrastructure and will instead only author expression strings for evaluation. An |
|
|
|
example of this typical use is the integration of SpEL into creating XML or annotated |
|
|
|
example of this typical use is the integration of SpEL into creating XML or annotated |
|
|
|
based bean definitions as shown in the section <<expressions-beandef,Expression support |
|
|
|
based bean definitions as shown in the section <<expressions-beandef,Expression support |
|
|
|
for defining bean definitions.>> |
|
|
|
for defining bean definitions>>. |
|
|
|
|
|
|
|
|
|
|
|
This chapter covers the features of the expression language, its API, and its language |
|
|
|
This chapter covers the features of the expression language, its API, and its language |
|
|
|
syntax. In several places an Inventor and Inventor's Society class are used as the |
|
|
|
syntax. In several places an `Inventor` and Inventor's `Society` classes are used as the |
|
|
|
target objects for expression evaluation. These class declarations and the data used to |
|
|
|
target objects for expression evaluation. These class declarations and the data used to |
|
|
|
populate them are listed at the end of the chapter. |
|
|
|
populate them are listed at the end of the chapter. |
|
|
|
|
|
|
|
|
|
|
|
@ -82,7 +82,7 @@ The following code introduces the SpEL API to evaluate the literal string expres |
|
|
|
The value of the message variable is simply 'Hello World'. |
|
|
|
The value of the message variable is simply 'Hello World'. |
|
|
|
|
|
|
|
|
|
|
|
The SpEL classes and interfaces you are most likely to use are located in the packages |
|
|
|
The SpEL classes and interfaces you are most likely to use are located in the packages |
|
|
|
`org.springframework.expression` and its sub packages and `spel.support`. |
|
|
|
`org.springframework.expression` and its sub packages such as `spel.support`. |
|
|
|
|
|
|
|
|
|
|
|
The interface `ExpressionParser` is responsible for parsing an expression string. In |
|
|
|
The interface `ExpressionParser` is responsible for parsing an expression string. In |
|
|
|
this example the expression string is a string literal denoted by the surrounding single |
|
|
|
this example the expression string is a string literal denoted by the surrounding single |
|
|
|
@ -119,8 +119,8 @@ as shown below. |
|
|
|
byte[] bytes = (byte[]) exp.getValue(); |
|
|
|
byte[] bytes = (byte[]) exp.getValue(); |
|
|
|
---- |
|
|
|
---- |
|
|
|
|
|
|
|
|
|
|
|
SpEL also supports nested properties using standard _dot_ notation, i.e. |
|
|
|
SpEL also supports nested properties using the standard _dot_ notation, i.e. |
|
|
|
prop1.prop2.prop3 and the setting of property values |
|
|
|
`prop1.prop2.prop3` and also the corresponding setting of property values. |
|
|
|
|
|
|
|
|
|
|
|
Public fields may also be accessed. |
|
|
|
Public fields may also be accessed. |
|
|
|
|
|
|
|
|
|
|
|
@ -181,36 +181,35 @@ create a boolean condition: |
|
|
|
=== `EvaluationContext` |
|
|
|
=== `EvaluationContext` |
|
|
|
|
|
|
|
|
|
|
|
The interface `EvaluationContext` is used when evaluating an expression to resolve |
|
|
|
The interface `EvaluationContext` is used when evaluating an expression to resolve |
|
|
|
properties, methods, fields, and to help perform type conversion. There are two |
|
|
|
properties, methods, or fields and to help perform type conversion. There are two |
|
|
|
out-of-the-box implementations. |
|
|
|
out-of-the-box implementations. |
|
|
|
|
|
|
|
|
|
|
|
* `SimpleEvaluationContext` -- exposes a subset of essential SpEL language features and |
|
|
|
* `SimpleEvaluationContext` -- exposes a subset of essential SpEL language features and |
|
|
|
configuration options, for categories of expressions that do not require the full extent |
|
|
|
configuration options, for categories of expressions that do not require the full extent |
|
|
|
of the SpEL language syntax and should be meaningfully restricted. Examples |
|
|
|
of the SpEL language syntax and should be meaningfully restricted. Examples include but |
|
|
|
include but are not limited to data binding expressions, property-based filters, and |
|
|
|
are not limited to data binding expressions, property-based filters, and others. |
|
|
|
others. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
* `StandardEvaluationContext` -- exposes the full set of SpEL language features and |
|
|
|
* `StandardEvaluationContext` -- exposes the full set of SpEL language features and |
|
|
|
configuration options. You may use it to specify a default root object, and to configure |
|
|
|
configuration options. You may use it to specify a default root object and to configure |
|
|
|
every available evaluation-related strategy. |
|
|
|
every available evaluation-related strategy. |
|
|
|
|
|
|
|
|
|
|
|
`SimpleEvaluationContext` is designed to support only a subset of the SpEL language syntax. |
|
|
|
`SimpleEvaluationContext` is designed to support only a subset of the SpEL language syntax. |
|
|
|
It excludes Java type references, constructors, and bean references. It also requires |
|
|
|
It _excludes_ Java type references, constructors, and bean references. It also requires |
|
|
|
explicit choosing the level of support for properties and methods in expressions. |
|
|
|
that one 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 of, 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 |
|
|
|
. Data binding properties for read and write. |
|
|
|
. Data binding properties for read and write |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[expressions-type-conversion]] |
|
|
|
[[expressions-type-conversion]] |
|
|
|
==== Type conversion |
|
|
|
==== Type conversion |
|
|
|
|
|
|
|
|
|
|
|
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 converters built in for common conversions but is also fully extensible so |
|
|
|
with many converters built in for common conversions but is also fully extensible so |
|
|
|
custom conversions between types can be added. Additionally it has the key capability |
|
|
|
custom conversions between types can be added. Additionally it has the key capability |
|
|
|
that it is generics aware. This means that when working with generic types in |
|
|
|
that it is generics aware. This means that when working with generic types in |
|
|
|
@ -289,22 +288,21 @@ It is also possible to configure the behaviour of the SpEL expression compiler. |
|
|
|
|
|
|
|
|
|
|
|
Spring Framework 4.1 includes a basic expression compiler. Expressions are usually |
|
|
|
Spring Framework 4.1 includes a basic expression compiler. 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 provide the optimum performance. For occasional expression usage |
|
|
|
does not provide optimum performance. For occasional expression usage |
|
|
|
this is fine, but when used by other components like Spring Integration, |
|
|
|
this is fine, but when used by other components like Spring Integration, |
|
|
|
performance can be very important and there is no real need for the dynamism. |
|
|
|
performance can be very important and there is no real need for the dynamism. |
|
|
|
|
|
|
|
|
|
|
|
The new SpEL compiler is intended to address this need. The |
|
|
|
The SpEL compiler is intended to address this need. The compiler will generate a |
|
|
|
compiler will generate a real Java class on the fly during evaluation that embodies the |
|
|
|
real Java class on the fly during evaluation that embodies the expression behavior |
|
|
|
expression behavior and use that to achieve much faster expression |
|
|
|
and use that to achieve much faster expression evaluation. Due to the lack of |
|
|
|
evaluation. Due to the lack of typing around expressions the compiler |
|
|
|
typing around expressions the compiler uses information gathered during the |
|
|
|
uses information gathered during the interpreted evaluations of an |
|
|
|
interpreted evaluations of an expression when performing compilation. For example, |
|
|
|
expression when performing compilation. For example, it does not know the type |
|
|
|
it does not know the type of a property reference purely from the expression but |
|
|
|
of a property reference purely from the expression but during the first |
|
|
|
during the first interpreted evaluation it will find out what it is. Of course, |
|
|
|
interpreted evaluation it will find out what it is. Of course, basing the |
|
|
|
basing the compilation on this information could cause trouble later if the types |
|
|
|
compilation on this information could cause trouble later if the types of |
|
|
|
of the various expression elements change over time. For this reason compilation |
|
|
|
the various expression elements change over time. For this reason compilation |
|
|
|
is best suited to expressions whose type information is not going to change on |
|
|
|
is best suited to expressions whose type information is not going to change |
|
|
|
repeated evaluations. |
|
|
|
on repeated evaluations. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
For a basic expression like this: |
|
|
|
For a basic expression like this: |
|
|
|
|
|
|
|
|
|
|
|
@ -365,24 +363,22 @@ After selecting a mode, use the `SpelParserConfiguration` to configure the parse |
|
|
|
When specifying the compiler mode it is also possible to specify a classloader (passing null is allowed). |
|
|
|
When specifying the compiler mode it is also possible to specify a classloader (passing null is allowed). |
|
|
|
Compiled expressions will be defined in a child classloader created under any that is supplied. |
|
|
|
Compiled expressions will be defined in a child classloader created under any that is supplied. |
|
|
|
It is important to ensure if a classloader is specified it can see all the types involved in |
|
|
|
It is important to ensure if a classloader is specified it can see all the types involved in |
|
|
|
the expression evaluation process. |
|
|
|
the expression evaluation process. If none is specified then a default classloader will be used |
|
|
|
If none is specified then a default classloader will be used (typically the context classloader for |
|
|
|
(typically the context classloader for the thread that is running during expression evaluation). |
|
|
|
the thread that is running during expression evaluation). |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The second way to configure the compiler is for use when SpEL is embedded inside some other |
|
|
|
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 via a configuration object. |
|
|
|
component and it may not be possible to configure via a configuration object. In these cases |
|
|
|
In these cases it is possible to use a system property. The property |
|
|
|
it is possible to use a system property. The property `spring.expression.compiler.mode` can be |
|
|
|
`spring.expression.compiler.mode` can be set to one of the `SpelCompilerMode` |
|
|
|
set to one of the `SpelCompilerMode` enum values (`off`, `immediate`, or `mixed`). |
|
|
|
enum values (`off`, `immediate`, or `mixed`). |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[expressions-compiler-limitations]] |
|
|
|
[[expressions-compiler-limitations]] |
|
|
|
==== Compiler limitations |
|
|
|
==== Compiler limitations |
|
|
|
|
|
|
|
|
|
|
|
With Spring Framework 4.1 the basic compilation framework is in place. However, the framework does not |
|
|
|
Since Spring Framework 4.1 the basic compilation framework is in place. However, the framework |
|
|
|
yet support compiling every kind of expression. The initial focus has been on the common expressions that are |
|
|
|
does not yet support compiling every kind of expression. The initial focus has been on the |
|
|
|
likely to be used in performance critical contexts. These kinds of expression cannot be compiled |
|
|
|
common expressions that are likely to be used in performance critical contexts. The following |
|
|
|
at the moment: |
|
|
|
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 |
|
|
|
@ -608,7 +604,7 @@ arrays and lists are obtained using square bracket notation. |
|
|
|
[subs="verbatim,quotes"] |
|
|
|
[subs="verbatim,quotes"] |
|
|
|
---- |
|
|
|
---- |
|
|
|
ExpressionParser parser = new SpelExpressionParser(); |
|
|
|
ExpressionParser parser = new SpelExpressionParser(); |
|
|
|
SimpleEvaluationContext context = SimpleEvaluationContext.create(); |
|
|
|
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build(); |
|
|
|
|
|
|
|
|
|
|
|
// Inventions Array |
|
|
|
// Inventions Array |
|
|
|
|
|
|
|
|
|
|
|
@ -779,11 +775,11 @@ expression based `matches` operator. |
|
|
|
|
|
|
|
|
|
|
|
// evaluates to true |
|
|
|
// evaluates to true |
|
|
|
boolean trueValue = parser.parseExpression( |
|
|
|
boolean trueValue = parser.parseExpression( |
|
|
|
"'5.00' matches '\^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class); |
|
|
|
"'5.00' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class); |
|
|
|
|
|
|
|
|
|
|
|
//evaluates to false |
|
|
|
//evaluates to false |
|
|
|
boolean falseValue = parser.parseExpression( |
|
|
|
boolean falseValue = parser.parseExpression( |
|
|
|
"'5.0067' matches '\^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class); |
|
|
|
"'5.0067' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class); |
|
|
|
---- |
|
|
|
---- |
|
|
|
|
|
|
|
|
|
|
|
[NOTE] |
|
|
|
[NOTE] |
|
|
|
@ -796,7 +792,7 @@ evaluates to `true`, as expected. |
|
|
|
Each symbolic operator can also be specified as a purely alphabetic equivalent. This |
|
|
|
Each symbolic operator can also be specified as a purely alphabetic equivalent. This |
|
|
|
avoids problems where the symbols used have special meaning for the document type in |
|
|
|
avoids problems where the symbols used have special meaning for the document type in |
|
|
|
which the expression is embedded (eg. an XML document). The textual equivalents are |
|
|
|
which the expression is embedded (eg. an XML document). The textual equivalents are |
|
|
|
shown here: `lt` (`<`), `gt` (`>`), `le` (`<=`), `ge` (`>=`), `eq` (`==`), |
|
|
|
shown here: `lt` (`<`), `gt` (`>`), `le` (`\<=`), `ge` (`>=`), `eq` (`==`), |
|
|
|
`ne` (`!=`), `div` (`/`), `mod` (`%`), `not` (`!`). These are case insensitive. |
|
|
|
`ne` (`!=`), `div` (`/`), `mod` (`%`), `not` (`!`). These are case insensitive. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -850,33 +846,33 @@ operators are demonstrated below. |
|
|
|
[subs="verbatim,quotes"] |
|
|
|
[subs="verbatim,quotes"] |
|
|
|
---- |
|
|
|
---- |
|
|
|
// Addition |
|
|
|
// Addition |
|
|
|
int two = parser.parseExpression("1 + 1").getValue(Integer.class); // 2 |
|
|
|
int two = parser.parseExpression("1 + 1").getValue(Integer.class); // 2 |
|
|
|
|
|
|
|
|
|
|
|
String testString = parser.parseExpression( |
|
|
|
String testString = parser.parseExpression( |
|
|
|
"'test' + ' ' + 'string'").getValue(String.class); // 'test string' |
|
|
|
"'test' + ' ' + 'string'").getValue(String.class); // 'test string' |
|
|
|
|
|
|
|
|
|
|
|
// Subtraction |
|
|
|
// Subtraction |
|
|
|
int four = parser.parseExpression("1 - -3").getValue(Integer.class); // 4 |
|
|
|
int four = parser.parseExpression("1 - -3").getValue(Integer.class); // 4 |
|
|
|
|
|
|
|
|
|
|
|
double d = parser.parseExpression("1000.00 - 1e4").getValue(Double.class); // -9000 |
|
|
|
double d = parser.parseExpression("1000.00 - 1e4").getValue(Double.class); // -9000 |
|
|
|
|
|
|
|
|
|
|
|
// Multiplication |
|
|
|
// Multiplication |
|
|
|
int six = parser.parseExpression("-2 * -3").getValue(Integer.class); // 6 |
|
|
|
int six = parser.parseExpression("-2 * -3").getValue(Integer.class); // 6 |
|
|
|
|
|
|
|
|
|
|
|
double twentyFour = parser.parseExpression("2.0 * 3e0 * 4").getValue(Double.class); // 24.0 |
|
|
|
double twentyFour = parser.parseExpression("2.0 * 3e0 * 4").getValue(Double.class); // 24.0 |
|
|
|
|
|
|
|
|
|
|
|
// Division |
|
|
|
// Division |
|
|
|
int minusTwo = parser.parseExpression("6 / -3").getValue(Integer.class); // -2 |
|
|
|
int minusTwo = parser.parseExpression("6 / -3").getValue(Integer.class); // -2 |
|
|
|
|
|
|
|
|
|
|
|
double one = parser.parseExpression("8.0 / 4e0 / 2").getValue(Double.class); // 1.0 |
|
|
|
double one = parser.parseExpression("8.0 / 4e0 / 2").getValue(Double.class); // 1.0 |
|
|
|
|
|
|
|
|
|
|
|
// Modulus |
|
|
|
// Modulus |
|
|
|
int three = parser.parseExpression("7 % 4").getValue(Integer.class); // 3 |
|
|
|
int three = parser.parseExpression("7 % 4").getValue(Integer.class); // 3 |
|
|
|
|
|
|
|
|
|
|
|
int one = parser.parseExpression("8 / 5 % 2").getValue(Integer.class); // 1 |
|
|
|
int one = parser.parseExpression("8 / 5 % 2").getValue(Integer.class); // 1 |
|
|
|
|
|
|
|
|
|
|
|
// Operator precedence |
|
|
|
// Operator precedence |
|
|
|
int minusTwentyOne = parser.parseExpression("1+2-3*8").getValue(Integer.class); // -21 |
|
|
|
int minusTwentyOne = parser.parseExpression("1+2-3*8").getValue(Integer.class); // -21 |
|
|
|
---- |
|
|
|
---- |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -891,14 +887,13 @@ done within a call to `setValue` but can also be done inside a call to `getValue |
|
|
|
[subs="verbatim,quotes"] |
|
|
|
[subs="verbatim,quotes"] |
|
|
|
---- |
|
|
|
---- |
|
|
|
Inventor inventor = new Inventor(); |
|
|
|
Inventor inventor = new Inventor(); |
|
|
|
SimpleEvaluationContext context = SimpleEvaluationContext.create(); |
|
|
|
EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build(); |
|
|
|
|
|
|
|
|
|
|
|
parser.parseExpression("Name").setValue(context, inventor, "Alexander Seovic2"); |
|
|
|
parser.parseExpression("Name").setValue(context, inventor, "Aleksandar Seovic"); |
|
|
|
|
|
|
|
|
|
|
|
// alternatively |
|
|
|
// alternatively |
|
|
|
|
|
|
|
|
|
|
|
String aleks = parser.parseExpression( |
|
|
|
String aleks = parser.parseExpression( |
|
|
|
"Name = 'Alexandar Seovic'").getValue(context, inventor, String.class); |
|
|
|
"Name = 'Aleksandar Seovic'").getValue(context, inventor, String.class); |
|
|
|
---- |
|
|
|
---- |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -953,28 +948,28 @@ used). |
|
|
|
=== Variables |
|
|
|
=== Variables |
|
|
|
|
|
|
|
|
|
|
|
Variables can be referenced in the expression using the syntax `#variableName`. Variables |
|
|
|
Variables can be referenced in the expression using the syntax `#variableName`. Variables |
|
|
|
are set using the method setVariable on `EvaluationContext` implementations. |
|
|
|
are set using the method `setVariable` on `EvaluationContext` implementations. |
|
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0] |
|
|
|
[source,java,indent=0] |
|
|
|
[subs="verbatim,quotes"] |
|
|
|
[subs="verbatim,quotes"] |
|
|
|
---- |
|
|
|
---- |
|
|
|
Inventor tesla = new Inventor("Nikola Tesla", "Serbian"); |
|
|
|
Inventor tesla = new Inventor("Nikola Tesla", "Serbian"); |
|
|
|
SimpleEvaluationContext context = SimpleEvaluationContext.create(); |
|
|
|
|
|
|
|
|
|
|
|
EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build(); |
|
|
|
context.setVariable("newName", "Mike Tesla"); |
|
|
|
context.setVariable("newName", "Mike Tesla"); |
|
|
|
|
|
|
|
|
|
|
|
parser.parseExpression("Name = #newName").getValue(context, tesla); |
|
|
|
parser.parseExpression("Name = #newName").getValue(context, tesla); |
|
|
|
|
|
|
|
System.out.println(tesla.getName()) // "Mike Tesla" |
|
|
|
System.out.println(tesla.getName()) // "Mike Tesla" |
|
|
|
|
|
|
|
---- |
|
|
|
---- |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[expressions-this-root]] |
|
|
|
[[expressions-this-root]] |
|
|
|
==== The #this and #root variables |
|
|
|
==== The #this and #root variables |
|
|
|
|
|
|
|
|
|
|
|
The variable #this is always defined and refers to the current evaluation object |
|
|
|
The variable `#this` is always defined and refers to the current evaluation object |
|
|
|
(against which unqualified references are resolved). The variable #root is always |
|
|
|
(against which unqualified references are resolved). The variable `#root` is always |
|
|
|
defined and refers to the root context object. Although #this may vary as components of |
|
|
|
defined and refers to the root context object. Although `#this` may vary as components of |
|
|
|
an expression are evaluated, #root always refers to the root. |
|
|
|
an expression are evaluated, `#root` always refers to the root. |
|
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0] |
|
|
|
[source,java,indent=0] |
|
|
|
[subs="verbatim,quotes"] |
|
|
|
[subs="verbatim,quotes"] |
|
|
|
@ -985,8 +980,8 @@ an expression are evaluated, #root always refers to the root. |
|
|
|
|
|
|
|
|
|
|
|
// create parser and set variable 'primes' as the array of integers |
|
|
|
// create parser and set variable 'primes' as the array of integers |
|
|
|
ExpressionParser parser = new SpelExpressionParser(); |
|
|
|
ExpressionParser parser = new SpelExpressionParser(); |
|
|
|
SimpleEvaluationContext context = SimpleEvaluationContext.create(); |
|
|
|
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataAccess(); |
|
|
|
context.setVariable("primes",primes); |
|
|
|
context.setVariable("primes", primes); |
|
|
|
|
|
|
|
|
|
|
|
// all prime numbers > 10 from the list (using selection ?{...}) |
|
|
|
// all prime numbers > 10 from the list (using selection ?{...}) |
|
|
|
// evaluates to [11, 13, 17] |
|
|
|
// evaluates to [11, 13, 17] |
|
|
|
@ -1007,7 +1002,7 @@ expression string. The function is registered through the `EvaluationContext`. |
|
|
|
---- |
|
|
|
---- |
|
|
|
Method method = ...; |
|
|
|
Method method = ...; |
|
|
|
|
|
|
|
|
|
|
|
SimpleEvaluationContext context = SimpleEvaluationContext.create(); |
|
|
|
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build(); |
|
|
|
context.setVariable("myFunction", method); |
|
|
|
context.setVariable("myFunction", method); |
|
|
|
---- |
|
|
|
---- |
|
|
|
|
|
|
|
|
|
|
|
@ -1019,7 +1014,7 @@ For example, given a utility method to reverse a string is shown below: |
|
|
|
public abstract class StringUtils { |
|
|
|
public abstract class StringUtils { |
|
|
|
|
|
|
|
|
|
|
|
public static String reverseString(String input) { |
|
|
|
public static String reverseString(String input) { |
|
|
|
StringBuilder backwards = new StringBuilder(); |
|
|
|
StringBuilder backwards = new StringBuilder(input.length()); |
|
|
|
for (int i = 0; i < input.length(); i++) |
|
|
|
for (int i = 0; i < input.length(); i++) |
|
|
|
backwards.append(input.charAt(input.length() - 1 - i)); |
|
|
|
backwards.append(input.charAt(input.length() - 1 - i)); |
|
|
|
} |
|
|
|
} |
|
|
|
@ -1034,13 +1029,13 @@ The above method can then be registered and used as follows: |
|
|
|
[subs="verbatim,quotes"] |
|
|
|
[subs="verbatim,quotes"] |
|
|
|
---- |
|
|
|
---- |
|
|
|
ExpressionParser parser = new SpelExpressionParser(); |
|
|
|
ExpressionParser parser = new SpelExpressionParser(); |
|
|
|
SimpleEvaluationContext context = SimpleEvaluationContext.create(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build(); |
|
|
|
context.setVariable("reverseString", |
|
|
|
context.setVariable("reverseString", |
|
|
|
StringUtils.class.getDeclaredMethod("reverseString", String.class)); |
|
|
|
StringUtils.class.getDeclaredMethod("reverseString", String.class)); |
|
|
|
|
|
|
|
|
|
|
|
String helloWorldReversed = parser.parseExpression( |
|
|
|
String helloWorldReversed = parser.parseExpression( |
|
|
|
"#reverseString('hello')").getValue(context, String.class); |
|
|
|
"#reverseString('hello')").getValue(context, String.class); |
|
|
|
---- |
|
|
|
---- |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -1049,26 +1044,26 @@ The above method can then be registered and used as follows: |
|
|
|
=== Bean references |
|
|
|
=== Bean references |
|
|
|
|
|
|
|
|
|
|
|
If the evaluation context has been configured with a bean resolver it is possible to |
|
|
|
If the evaluation context has been configured with a bean resolver it is possible to |
|
|
|
lookup beans from an expression using the (@) symbol. |
|
|
|
look up beans from an expression using the `@` symbol. |
|
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0] |
|
|
|
[source,java,indent=0] |
|
|
|
[subs="verbatim,quotes"] |
|
|
|
[subs="verbatim,quotes"] |
|
|
|
---- |
|
|
|
---- |
|
|
|
ExpressionParser parser = new SpelExpressionParser(); |
|
|
|
ExpressionParser parser = new SpelExpressionParser(); |
|
|
|
StandardEvaluationContext context = StandardEvaluationContext.create(); |
|
|
|
StandardEvaluationContext context = new StandardEvaluationContext(); |
|
|
|
context.setBeanResolver(new MyBeanResolver()); |
|
|
|
context.setBeanResolver(new MyBeanResolver()); |
|
|
|
|
|
|
|
|
|
|
|
// This will end up calling resolve(context,"foo") on MyBeanResolver during evaluation |
|
|
|
// This will end up calling resolve(context,"foo") on MyBeanResolver during evaluation |
|
|
|
Object bean = parser.parseExpression("@foo").getValue(context); |
|
|
|
Object bean = parser.parseExpression("@foo").getValue(context); |
|
|
|
---- |
|
|
|
---- |
|
|
|
|
|
|
|
|
|
|
|
To access a factory bean itself, the bean name should instead be prefixed with a (&) symbol. |
|
|
|
To access a factory bean itself, the bean name should instead be prefixed with an `&` symbol. |
|
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0] |
|
|
|
[source,java,indent=0] |
|
|
|
[subs="verbatim,quotes"] |
|
|
|
[subs="verbatim,quotes"] |
|
|
|
---- |
|
|
|
---- |
|
|
|
ExpressionParser parser = new SpelExpressionParser(); |
|
|
|
ExpressionParser parser = new SpelExpressionParser(); |
|
|
|
StandardEvaluationContext context = StandardEvaluationContext.create(); |
|
|
|
StandardEvaluationContext context = new StandardEvaluationContext(); |
|
|
|
context.setBeanResolver(new MyBeanResolver()); |
|
|
|
context.setBeanResolver(new MyBeanResolver()); |
|
|
|
|
|
|
|
|
|
|
|
// This will end up calling resolve(context,"&foo") on MyBeanResolver during evaluation |
|
|
|
// This will end up calling resolve(context,"&foo") on MyBeanResolver during evaluation |
|
|
|
@ -1089,8 +1084,8 @@ the expression. A minimal example is: |
|
|
|
"false ? 'trueExp' : 'falseExp'").getValue(String.class); |
|
|
|
"false ? 'trueExp' : 'falseExp'").getValue(String.class); |
|
|
|
---- |
|
|
|
---- |
|
|
|
|
|
|
|
|
|
|
|
In this case, the boolean false results in returning the string value 'falseExp'. A more |
|
|
|
In this case, the boolean false results in returning the string value `'falseExp'`. |
|
|
|
realistic example is shown below. |
|
|
|
A more realistic example is shown below. |
|
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0] |
|
|
|
[source,java,indent=0] |
|
|
|
[subs="verbatim,quotes"] |
|
|
|
[subs="verbatim,quotes"] |
|
|
|
@ -1114,19 +1109,20 @@ ternary operator. |
|
|
|
[[expressions-operator-elvis]] |
|
|
|
[[expressions-operator-elvis]] |
|
|
|
=== The Elvis Operator |
|
|
|
=== The Elvis Operator |
|
|
|
|
|
|
|
|
|
|
|
The Elvis operator is a shortening of the ternary operator syntax and is used in the |
|
|
|
The Elvis operator is a shortening of the ternary operator syntax and is used in |
|
|
|
http://www.groovy-lang.org/operators.html#_elvis_operator[Groovy] language. |
|
|
|
the http://www.groovy-lang.org/operators.html#_elvis_operator[Groovy] language. |
|
|
|
With the ternary operator syntax you usually have to repeat a variable twice, for |
|
|
|
With the ternary operator syntax, you usually have to repeat a variable twice, |
|
|
|
example: |
|
|
|
as the following example shows: |
|
|
|
|
|
|
|
|
|
|
|
[source,groovy,indent=0] |
|
|
|
[source,groovy,indent=0] |
|
|
|
[subs="verbatim,quotes"] |
|
|
|
[subs="verbatim,quotes"] |
|
|
|
---- |
|
|
|
---- |
|
|
|
String name = "Elvis Presley"; |
|
|
|
String name = "Elvis Presley"; |
|
|
|
String displayName = name != null ? name : "Unknown"; |
|
|
|
String displayName = (name != null ? name : "Unknown"); |
|
|
|
---- |
|
|
|
---- |
|
|
|
|
|
|
|
|
|
|
|
Instead you can use the Elvis operator, named for the resemblance to Elvis' hair style. |
|
|
|
Instead, you can use the Elvis operator (named for the resemblance to Elvis' hair style). |
|
|
|
|
|
|
|
The following example shows how to use the Elvis operator: |
|
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0] |
|
|
|
[source,java,indent=0] |
|
|
|
[subs="verbatim,quotes"] |
|
|
|
[subs="verbatim,quotes"] |
|
|
|
@ -1134,29 +1130,24 @@ Instead you can use the Elvis operator, named for the resemblance to Elvis' hair |
|
|
|
ExpressionParser parser = new SpelExpressionParser(); |
|
|
|
ExpressionParser parser = new SpelExpressionParser(); |
|
|
|
|
|
|
|
|
|
|
|
String name = parser.parseExpression("name?:'Unknown'").getValue(String.class); |
|
|
|
String name = parser.parseExpression("name?:'Unknown'").getValue(String.class); |
|
|
|
|
|
|
|
System.out.println(name); // 'Unknown' |
|
|
|
System.out.println(name); // 'Unknown' |
|
|
|
|
|
|
|
---- |
|
|
|
---- |
|
|
|
|
|
|
|
|
|
|
|
Here is a more complex example. |
|
|
|
The following listing shows a more complex example: |
|
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0] |
|
|
|
[source,java,indent=0] |
|
|
|
[subs="verbatim,quotes"] |
|
|
|
[subs="verbatim,quotes"] |
|
|
|
---- |
|
|
|
---- |
|
|
|
ExpressionParser parser = new SpelExpressionParser(); |
|
|
|
ExpressionParser parser = new SpelExpressionParser(); |
|
|
|
|
|
|
|
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build(); |
|
|
|
|
|
|
|
|
|
|
|
Inventor tesla = new Inventor("Nikola Tesla", "Serbian"); |
|
|
|
Inventor tesla = new Inventor("Nikola Tesla", "Serbian"); |
|
|
|
SimpleEvaluationContext context = SimpleEvaluationContext.create(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
String name = parser.parseExpression("Name?:'Elvis Presley'").getValue(context, tesla, String.class); |
|
|
|
String name = parser.parseExpression("Name?:'Elvis Presley'").getValue(context, tesla, String.class); |
|
|
|
|
|
|
|
System.out.println(name); // Nikola Tesla |
|
|
|
System.out.println(name); // Nikola Tesla |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tesla.setName(null); |
|
|
|
tesla.setName(null); |
|
|
|
|
|
|
|
|
|
|
|
name = parser.parseExpression("Name?:'Elvis Presley'").getValue(context, tesla, String.class); |
|
|
|
name = parser.parseExpression("Name?:'Elvis Presley'").getValue(context, tesla, String.class); |
|
|
|
|
|
|
|
System.out.println(name); // Elvis Presley |
|
|
|
System.out.println(name); // Elvis Presley |
|
|
|
|
|
|
|
---- |
|
|
|
---- |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -1174,20 +1165,17 @@ safe navigation operator will simply return null instead of throwing an exceptio |
|
|
|
[subs="verbatim,quotes"] |
|
|
|
[subs="verbatim,quotes"] |
|
|
|
---- |
|
|
|
---- |
|
|
|
ExpressionParser parser = new SpelExpressionParser(); |
|
|
|
ExpressionParser parser = new SpelExpressionParser(); |
|
|
|
|
|
|
|
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build(); |
|
|
|
|
|
|
|
|
|
|
|
Inventor tesla = new Inventor("Nikola Tesla", "Serbian"); |
|
|
|
Inventor tesla = new Inventor("Nikola Tesla", "Serbian"); |
|
|
|
tesla.setPlaceOfBirth(new PlaceOfBirth("Smiljan")); |
|
|
|
tesla.setPlaceOfBirth(new PlaceOfBirth("Smiljan")); |
|
|
|
|
|
|
|
|
|
|
|
SimpleEvaluationContext context = SimpleEvaluationContext.create(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
String city = parser.parseExpression("PlaceOfBirth?.City").getValue(context, tesla, String.class); |
|
|
|
String city = parser.parseExpression("PlaceOfBirth?.City").getValue(context, tesla, String.class); |
|
|
|
System.out.println(city); // Smiljan |
|
|
|
System.out.println(city); // Smiljan |
|
|
|
|
|
|
|
|
|
|
|
tesla.setPlaceOfBirth(null); |
|
|
|
tesla.setPlaceOfBirth(null); |
|
|
|
|
|
|
|
|
|
|
|
city = parser.parseExpression("PlaceOfBirth?.City").getValue(context, tesla, String.class); |
|
|
|
city = parser.parseExpression("PlaceOfBirth?.City").getValue(context, tesla, String.class); |
|
|
|
|
|
|
|
System.out.println(city); // null - does not throw NullPointerException!!! |
|
|
|
System.out.println(city); // null - does not throw NullPointerException!!! |
|
|
|
|
|
|
|
---- |
|
|
|
---- |
|
|
|
|
|
|
|
|
|
|
|
[NOTE] |
|
|
|
[NOTE] |
|
|
|
@ -1240,7 +1228,8 @@ where the entry value is less than 27. |
|
|
|
|
|
|
|
|
|
|
|
In addition to returning all the selected elements, it is possible to retrieve just the |
|
|
|
In addition to returning all the selected elements, it is possible to retrieve just the |
|
|
|
first or the last value. To obtain the first entry matching the selection the syntax is |
|
|
|
first or the last value. To obtain the first entry matching the selection the syntax is |
|
|
|
`^[...]` whilst to obtain the last matching selection the syntax is `$[...]`. |
|
|
|
`.^[selectionExpression]` whilst to obtain the last matching selection the syntax is |
|
|
|
|
|
|
|
`.$[selectionExpression]`. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -1248,9 +1237,9 @@ first or the last value. To obtain the first entry matching the selection the sy |
|
|
|
=== Collection Projection |
|
|
|
=== Collection Projection |
|
|
|
|
|
|
|
|
|
|
|
Projection allows a collection to drive the evaluation of a sub-expression and the |
|
|
|
Projection allows a collection to drive the evaluation of a sub-expression and the |
|
|
|
result is a new collection. The syntax for projection is `![projectionExpression]`. Most |
|
|
|
result is a new collection. The syntax for projection is `.![projectionExpression]`. |
|
|
|
easily understood by example, suppose we have a list of inventors but want the list of |
|
|
|
Most easily understood by example, suppose we have a list of inventors but want the list |
|
|
|
cities where they were born. Effectively we want to evaluate 'placeOfBirth.city' for |
|
|
|
of cities where they were born. Effectively we want to evaluate 'placeOfBirth.city' for |
|
|
|
every entry in the inventor list. Using projection: |
|
|
|
every entry in the inventor list. Using projection: |
|
|
|
|
|
|
|
|
|
|
|
[source,java,indent=0] |
|
|
|
[source,java,indent=0] |
|
|
|
@ -1284,9 +1273,9 @@ define, a common choice is to use `#{ }` as the delimiters. For example, |
|
|
|
// evaluates to "random number is 0.7038186818312008" |
|
|
|
// evaluates to "random number is 0.7038186818312008" |
|
|
|
---- |
|
|
|
---- |
|
|
|
|
|
|
|
|
|
|
|
The string is evaluated by concatenating the literal text 'random number is ' with the |
|
|
|
The string is evaluated by concatenating the literal text `'random number is '` with the |
|
|
|
result of evaluating the expression inside the #{ } delimiter, in this case the result |
|
|
|
result of evaluating the expression inside the `#{ }` delimiter, in this case the result |
|
|
|
of calling that random() method. The second argument to the method `parseExpression()` |
|
|
|
of calling that `random()` method. The second argument to the method `parseExpression()` |
|
|
|
is of the type `ParserContext`. The `ParserContext` interface is used to influence how |
|
|
|
is of the type `ParserContext`. The `ParserContext` interface is used to influence how |
|
|
|
the expression is parsed in order to support the expression templating functionality. |
|
|
|
the expression is parsed in order to support the expression templating functionality. |
|
|
|
The definition of `TemplateParserContext` is shown below. |
|
|
|
The definition of `TemplateParserContext` is shown below. |
|
|
|
|