0
# Expression Evaluation
1
2
This document covers SpEL's expression evaluation capabilities, including the core interfaces, standard implementations, and evaluation patterns.
3
4
## Core Expression Evaluation
5
6
### SpelExpressionParser Class
7
8
```java
9
public class SpelExpressionParser extends TemplateAwareExpressionParser {
10
public SpelExpressionParser();
11
public SpelExpressionParser(SpelParserConfiguration configuration);
12
13
public SpelExpression parseRaw(String expressionString) throws ParseException;
14
// Inherited from ExpressionParser:
15
public Expression parseExpression(String expressionString) throws ParseException;
16
public Expression parseExpression(String expressionString, ParserContext context)
17
throws ParseException;
18
}
19
```
20
{ .api }
21
22
The SpelExpressionParser is thread-safe and should be reused across multiple parsing operations for better performance.
23
24
### SpelExpression Class
25
26
```java
27
public class SpelExpression implements Expression {
28
// Configuration
29
public void setEvaluationContext(EvaluationContext evaluationContext);
30
public EvaluationContext getEvaluationContext();
31
32
// Compilation
33
public boolean compileExpression();
34
public void revertToInterpreted();
35
36
// AST Access
37
public SpelNode getAST();
38
public String toStringAST();
39
40
// Expression Interface Methods
41
public String getExpressionString();
42
43
public Object getValue() throws EvaluationException;
44
public <T> T getValue(Class<T> desiredResultType) throws EvaluationException;
45
public Object getValue(Object rootObject) throws EvaluationException;
46
public Object getValue(EvaluationContext context) throws EvaluationException;
47
public Object getValue(EvaluationContext context, Object rootObject) throws EvaluationException;
48
public <T> T getValue(EvaluationContext context, Object rootObject, Class<T> expectedResultType)
49
throws EvaluationException;
50
51
public Class<?> getValueType() throws EvaluationException;
52
public Class<?> getValueType(Object rootObject) throws EvaluationException;
53
public Class<?> getValueType(EvaluationContext context) throws EvaluationException;
54
public Class<?> getValueType(EvaluationContext context, Object rootObject)
55
throws EvaluationException;
56
57
public TypeDescriptor getValueTypeDescriptor() throws EvaluationException;
58
public TypeDescriptor getValueTypeDescriptor(Object rootObject) throws EvaluationException;
59
public TypeDescriptor getValueTypeDescriptor(EvaluationContext context) throws EvaluationException;
60
public TypeDescriptor getValueTypeDescriptor(EvaluationContext context, Object rootObject)
61
throws EvaluationException;
62
63
public boolean isWritable(Object rootObject) throws EvaluationException;
64
public boolean isWritable(EvaluationContext context) throws EvaluationException;
65
public boolean isWritable(EvaluationContext context, Object rootObject) throws EvaluationException;
66
67
public void setValue(Object rootObject, Object value) throws EvaluationException;
68
public void setValue(EvaluationContext context, Object value) throws EvaluationException;
69
public void setValue(EvaluationContext context, Object rootObject, Object value)
70
throws EvaluationException;
71
}
72
```
73
{ .api }
74
75
## Parser Context Support
76
77
### ParserContext Interface
78
79
```java
80
public interface ParserContext {
81
boolean isTemplate();
82
String getExpressionPrefix();
83
String getExpressionSuffix();
84
85
// Constants
86
ParserContext TEMPLATE_EXPRESSION = new TemplateParserContext();
87
}
88
```
89
{ .api }
90
91
### TemplateParserContext Class
92
93
```java
94
public class TemplateParserContext implements ParserContext {
95
public TemplateParserContext();
96
public TemplateParserContext(String expressionPrefix, String expressionSuffix);
97
98
public boolean isTemplate();
99
public String getExpressionPrefix();
100
public String getExpressionSuffix();
101
}
102
```
103
{ .api }
104
105
## Expression Types
106
107
### LiteralExpression Class
108
109
```java
110
public class LiteralExpression implements Expression {
111
public LiteralExpression(String literalValue);
112
113
public String getExpressionString();
114
public Object getValue() throws EvaluationException;
115
public <T> T getValue(Class<T> expectedResultType) throws EvaluationException;
116
public Object getValue(Object rootObject) throws EvaluationException;
117
public Object getValue(EvaluationContext context) throws EvaluationException;
118
public Object getValue(EvaluationContext context, Object rootObject) throws EvaluationException;
119
public <T> T getValue(EvaluationContext context, Object rootObject, Class<T> expectedResultType)
120
throws EvaluationException;
121
122
public Class<?> getValueType();
123
public Class<?> getValueType(Object rootObject);
124
public Class<?> getValueType(EvaluationContext context);
125
public Class<?> getValueType(EvaluationContext context, Object rootObject);
126
127
public TypeDescriptor getValueTypeDescriptor();
128
public TypeDescriptor getValueTypeDescriptor(Object rootObject);
129
public TypeDescriptor getValueTypeDescriptor(EvaluationContext context);
130
public TypeDescriptor getValueTypeDescriptor(EvaluationContext context, Object rootObject);
131
132
public boolean isWritable(Object rootObject);
133
public boolean isWritable(EvaluationContext context);
134
public boolean isWritable(EvaluationContext context, Object rootObject);
135
136
public void setValue(Object rootObject, Object value);
137
public void setValue(EvaluationContext context, Object value);
138
public void setValue(EvaluationContext context, Object rootObject, Object value);
139
}
140
```
141
{ .api }
142
143
### CompositeStringExpression Class
144
145
```java
146
public class CompositeStringExpression implements Expression {
147
public CompositeStringExpression(String expressionString, Expression[] expressions);
148
149
// Implements all Expression interface methods
150
// Evaluates each sub-expression and concatenates results as strings
151
}
152
```
153
{ .api }
154
155
## Expression Language Features
156
157
### Basic Syntax Examples
158
159
```java
160
ExpressionParser parser = new SpelExpressionParser();
161
162
// Literals
163
Expression exp = parser.parseExpression("'Hello World'");
164
String result = exp.getValue(String.class); // "Hello World"
165
166
exp = parser.parseExpression("42");
167
Integer number = exp.getValue(Integer.class); // 42
168
169
exp = parser.parseExpression("true");
170
Boolean bool = exp.getValue(Boolean.class); // true
171
172
// Mathematical operations
173
exp = parser.parseExpression("2 + 3 * 4");
174
Integer math = exp.getValue(Integer.class); // 14
175
176
exp = parser.parseExpression("10 / 3.0");
177
Double division = exp.getValue(Double.class); // 3.3333333333333335
178
179
// String operations
180
exp = parser.parseExpression("'Hello' + ' ' + 'World'");
181
String concat = exp.getValue(String.class); // "Hello World"
182
183
// Logical operations
184
exp = parser.parseExpression("true and false");
185
Boolean logical = exp.getValue(Boolean.class); // false
186
187
exp = parser.parseExpression("2 > 1 ? 'yes' : 'no'");
188
String conditional = exp.getValue(String.class); // "yes"
189
```
190
{ .api }
191
192
### Property Access
193
194
```java
195
public class Person {
196
private String name;
197
private int age;
198
private Address address;
199
// getters and setters...
200
}
201
202
public class Address {
203
private String city;
204
private String country;
205
// getters and setters...
206
}
207
208
Person person = new Person();
209
person.setName("John");
210
person.setAge(30);
211
212
ExpressionParser parser = new SpelExpressionParser();
213
214
// Direct property access
215
Expression exp = parser.parseExpression("name");
216
String name = exp.getValue(person, String.class); // "John"
217
218
// Method invocation
219
exp = parser.parseExpression("name.toUpperCase()");
220
String upperName = exp.getValue(person, String.class); // "JOHN"
221
222
// Nested property access
223
exp = parser.parseExpression("address?.city");
224
String city = exp.getValue(person, String.class); // null (safe navigation)
225
226
// Property assignment (requires StandardEvaluationContext)
227
StandardEvaluationContext context = new StandardEvaluationContext(person);
228
exp = parser.parseExpression("name");
229
exp.setValue(context, "Jane");
230
```
231
{ .api }
232
233
### Collection and Array Access
234
235
```java
236
List<String> names = Arrays.asList("John", "Jane", "Bob");
237
Map<String, Integer> ages = Map.of("John", 30, "Jane", 25, "Bob", 35);
238
int[] numbers = {1, 2, 3, 4, 5};
239
240
ExpressionParser parser = new SpelExpressionParser();
241
StandardEvaluationContext context = new StandardEvaluationContext();
242
context.setVariable("names", names);
243
context.setVariable("ages", ages);
244
context.setVariable("numbers", numbers);
245
246
// List access
247
Expression exp = parser.parseExpression("#names[0]");
248
String firstName = exp.getValue(context, String.class); // "John"
249
250
// Map access
251
exp = parser.parseExpression("#ages['John']");
252
Integer age = exp.getValue(context, Integer.class); // 30
253
254
// Array access
255
exp = parser.parseExpression("#numbers[2]");
256
Integer number = exp.getValue(context, Integer.class); // 3
257
258
// Collection methods
259
exp = parser.parseExpression("#names.size()");
260
Integer size = exp.getValue(context, Integer.class); // 3
261
262
// Collection filtering and projection
263
exp = parser.parseExpression("#names.?[length() > 3]"); // Filter
264
List<String> filtered = (List<String>) exp.getValue(context); // ["John", "Jane"]
265
266
exp = parser.parseExpression("#names.![toUpperCase()]"); // Projection
267
List<String> projected = (List<String>) exp.getValue(context); // ["JOHN", "JANE", "BOB"]
268
```
269
{ .api }
270
271
### Variables and Functions
272
273
```java
274
StandardEvaluationContext context = new StandardEvaluationContext();
275
276
// Setting variables
277
context.setVariable("greeting", "Hello");
278
context.setVariable("name", "World");
279
280
// Using variables
281
ExpressionParser parser = new SpelExpressionParser();
282
Expression exp = parser.parseExpression("#greeting + ' ' + #name");
283
String message = exp.getValue(context, String.class); // "Hello World"
284
285
// Registering functions
286
context.registerFunction("reverse",
287
String.class.getDeclaredMethod("valueOf", Object.class));
288
289
exp = parser.parseExpression("#reverse('hello')");
290
String reversed = exp.getValue(context, String.class);
291
292
// Built-in variables (when available)
293
exp = parser.parseExpression("#this"); // Current object
294
exp = parser.parseExpression("#root"); // Root object
295
```
296
{ .api }
297
298
### Type References
299
300
```java
301
ExpressionParser parser = new SpelExpressionParser();
302
StandardEvaluationContext context = new StandardEvaluationContext();
303
304
// Type references
305
Expression exp = parser.parseExpression("T(java.lang.Math).PI");
306
Double pi = exp.getValue(context, Double.class); // 3.141592653589793
307
308
exp = parser.parseExpression("T(java.lang.Math).max(2, 3)");
309
Integer max = exp.getValue(context, Integer.class); // 3
310
311
// Constructor invocation
312
exp = parser.parseExpression("new java.util.Date()");
313
Date date = exp.getValue(context, Date.class);
314
315
exp = parser.parseExpression("new String('hello').toUpperCase()");
316
String upper = exp.getValue(context, String.class); // "HELLO"
317
```
318
{ .api }
319
320
## Advanced Evaluation Patterns
321
322
### Template Expressions
323
324
```java
325
ExpressionParser parser = new SpelExpressionParser();
326
Map<String, Object> vars = new HashMap<>();
327
vars.put("name", "John");
328
vars.put("age", 30);
329
330
StandardEvaluationContext context = new StandardEvaluationContext();
331
context.setVariables(vars);
332
333
// Template with multiple expressions
334
String template = "Hello #{#name}, you are #{#age} years old and next year you'll be #{#age + 1}!";
335
Expression exp = parser.parseExpression(template, ParserContext.TEMPLATE_EXPRESSION);
336
String result = exp.getValue(context, String.class);
337
// "Hello John, you are 30 years old and next year you'll be 31!"
338
```
339
{ .api }
340
341
### Safe Navigation
342
343
```java
344
public class Customer {
345
private Address address;
346
// getters and setters...
347
}
348
349
Customer customer = new Customer(); // address is null
350
351
ExpressionParser parser = new SpelExpressionParser();
352
353
// Safe navigation operator (?.): prevents NullPointerException
354
Expression exp = parser.parseExpression("address?.city");
355
String city = exp.getValue(customer, String.class); // null (no exception)
356
357
// Without safe navigation (would throw NullPointerException)
358
// exp = parser.parseExpression("address.city"); // Throws exception
359
```
360
{ .api }
361
362
### Elvis Operator
363
364
```java
365
ExpressionParser parser = new SpelExpressionParser();
366
StandardEvaluationContext context = new StandardEvaluationContext();
367
context.setVariable("name", null);
368
369
// Elvis operator (?:): provides default value for null
370
Expression exp = parser.parseExpression("#name ?: 'Unknown'");
371
String name = exp.getValue(context, String.class); // "Unknown"
372
373
context.setVariable("name", "John");
374
name = exp.getValue(context, String.class); // "John"
375
```
376
{ .api }
377
378
### Regular Expressions
379
380
```java
381
ExpressionParser parser = new SpelExpressionParser();
382
StandardEvaluationContext context = new StandardEvaluationContext();
383
context.setVariable("text", "Hello World");
384
385
// Pattern matching
386
Expression exp = parser.parseExpression("#text matches '[A-Z].*'");
387
Boolean matches = exp.getValue(context, Boolean.class); // true
388
389
// Case-insensitive matching
390
exp = parser.parseExpression("#text matches '(?i)hello.*'");
391
matches = exp.getValue(context, Boolean.class); // true
392
```
393
{ .api }
394
395
## Performance Optimization
396
397
### Expression Compilation
398
399
```java
400
// Enable compilation for better performance
401
SpelParserConfiguration config = new SpelParserConfiguration(
402
SpelCompilerMode.IMMEDIATE,
403
Thread.currentThread().getContextClassLoader()
404
);
405
SpelExpressionParser parser = new SpelExpressionParser(config);
406
407
SpelExpression expression = parser.parseRaw("name.toUpperCase() + age.toString()");
408
409
// First evaluation interprets the expression
410
String result1 = expression.getValue(person, String.class);
411
412
// Subsequent evaluations use compiled bytecode (much faster)
413
String result2 = expression.getValue(person, String.class);
414
415
// Check if expression is compiled
416
boolean isCompiled = expression.compileExpression(); // true if successfully compiled
417
418
// Revert to interpreted mode if needed
419
expression.revertToInterpreted();
420
```
421
{ .api }
422
423
### Expression Caching
424
425
```java
426
// Cache frequently used expressions
427
public class ExpressionCache {
428
private final Map<String, Expression> cache = new ConcurrentHashMap<>();
429
private final ExpressionParser parser = new SpelExpressionParser();
430
431
public Expression getExpression(String expressionString) {
432
return cache.computeIfAbsent(expressionString, parser::parseExpression);
433
}
434
}
435
436
// Usage
437
ExpressionCache cache = new ExpressionCache();
438
Expression exp = cache.getExpression("name.toUpperCase()");
439
String result = exp.getValue(person, String.class);
440
```
441
{ .api }
442
443
## Best Practices
444
445
1. **Reuse Parsers**: SpelExpressionParser is thread-safe and expensive to create
446
2. **Cache Expressions**: Cache compiled expressions for repeated use
447
3. **Use Compilation**: Enable compilation for frequently-evaluated expressions
448
4. **Choose Appropriate Context**: Use SimpleEvaluationContext for better security and performance in data-binding scenarios
449
5. **Handle Exceptions**: Always handle ParseException and EvaluationException appropriately
450
6. **Safe Navigation**: Use safe navigation operator (?.) to avoid NullPointerException
451
7. **Type Safety**: Specify expected result types when calling getValue()
452
453
## Thread Safety Notes
454
455
- **SpelExpressionParser**: Thread-safe, can be shared across threads
456
- **SpelExpression**: Thread-safe for evaluation, but configuration methods should not be called concurrently
457
- **EvaluationContext**: Generally not thread-safe, create separate instances per thread or evaluation
458
- **Compilation**: Compiled expressions are thread-safe for evaluation