CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-springframework--spring-expression

Spring Expression Language (SpEL) provides a powerful expression language for querying and manipulating object graphs at runtime.

Pending
Overview
Eval results
Files

expression-evaluation.mddocs/

Expression Evaluation

This document covers SpEL's expression evaluation capabilities, including the core interfaces, standard implementations, and evaluation patterns.

Core Expression Evaluation

SpelExpressionParser Class

public class SpelExpressionParser extends TemplateAwareExpressionParser {
    public SpelExpressionParser();
    public SpelExpressionParser(SpelParserConfiguration configuration);
    
    public SpelExpression parseRaw(String expressionString) throws ParseException;
    // Inherited from ExpressionParser:
    public Expression parseExpression(String expressionString) throws ParseException;
    public Expression parseExpression(String expressionString, ParserContext context) 
        throws ParseException;
}

{ .api }

The SpelExpressionParser is thread-safe and should be reused across multiple parsing operations for better performance.

SpelExpression Class

public class SpelExpression implements Expression {
    // Configuration
    public void setEvaluationContext(EvaluationContext evaluationContext);
    public EvaluationContext getEvaluationContext();
    
    // Compilation
    public boolean compileExpression();
    public void revertToInterpreted();
    
    // AST Access
    public SpelNode getAST();
    public String toStringAST();
    
    // Expression Interface Methods
    public String getExpressionString();
    
    public Object getValue() throws EvaluationException;
    public <T> T getValue(Class<T> desiredResultType) throws EvaluationException;
    public Object getValue(Object rootObject) throws EvaluationException;
    public Object getValue(EvaluationContext context) throws EvaluationException;
    public Object getValue(EvaluationContext context, Object rootObject) throws EvaluationException;
    public <T> T getValue(EvaluationContext context, Object rootObject, Class<T> expectedResultType) 
        throws EvaluationException;
    
    public Class<?> getValueType() throws EvaluationException;
    public Class<?> getValueType(Object rootObject) throws EvaluationException;
    public Class<?> getValueType(EvaluationContext context) throws EvaluationException;
    public Class<?> getValueType(EvaluationContext context, Object rootObject) 
        throws EvaluationException;
    
    public TypeDescriptor getValueTypeDescriptor() throws EvaluationException;
    public TypeDescriptor getValueTypeDescriptor(Object rootObject) throws EvaluationException;
    public TypeDescriptor getValueTypeDescriptor(EvaluationContext context) throws EvaluationException;
    public TypeDescriptor getValueTypeDescriptor(EvaluationContext context, Object rootObject) 
        throws EvaluationException;
    
    public boolean isWritable(Object rootObject) throws EvaluationException;
    public boolean isWritable(EvaluationContext context) throws EvaluationException;
    public boolean isWritable(EvaluationContext context, Object rootObject) throws EvaluationException;
    
    public void setValue(Object rootObject, Object value) throws EvaluationException;
    public void setValue(EvaluationContext context, Object value) throws EvaluationException;
    public void setValue(EvaluationContext context, Object rootObject, Object value) 
        throws EvaluationException;
}

{ .api }

Parser Context Support

ParserContext Interface

public interface ParserContext {
    boolean isTemplate();
    String getExpressionPrefix();
    String getExpressionSuffix();
    
    // Constants
    ParserContext TEMPLATE_EXPRESSION = new TemplateParserContext();
}

{ .api }

TemplateParserContext Class

public class TemplateParserContext implements ParserContext {
    public TemplateParserContext();
    public TemplateParserContext(String expressionPrefix, String expressionSuffix);
    
    public boolean isTemplate();
    public String getExpressionPrefix();
    public String getExpressionSuffix();
}

{ .api }

Expression Types

LiteralExpression Class

public class LiteralExpression implements Expression {
    public LiteralExpression(String literalValue);
    
    public String getExpressionString();
    public Object getValue() throws EvaluationException;
    public <T> T getValue(Class<T> expectedResultType) throws EvaluationException;
    public Object getValue(Object rootObject) throws EvaluationException;
    public Object getValue(EvaluationContext context) throws EvaluationException;
    public Object getValue(EvaluationContext context, Object rootObject) throws EvaluationException;
    public <T> T getValue(EvaluationContext context, Object rootObject, Class<T> expectedResultType)
        throws EvaluationException;
    
    public Class<?> getValueType();
    public Class<?> getValueType(Object rootObject);
    public Class<?> getValueType(EvaluationContext context);
    public Class<?> getValueType(EvaluationContext context, Object rootObject);
    
    public TypeDescriptor getValueTypeDescriptor();
    public TypeDescriptor getValueTypeDescriptor(Object rootObject);
    public TypeDescriptor getValueTypeDescriptor(EvaluationContext context);
    public TypeDescriptor getValueTypeDescriptor(EvaluationContext context, Object rootObject);
    
    public boolean isWritable(Object rootObject);
    public boolean isWritable(EvaluationContext context);
    public boolean isWritable(EvaluationContext context, Object rootObject);
    
    public void setValue(Object rootObject, Object value);
    public void setValue(EvaluationContext context, Object value);
    public void setValue(EvaluationContext context, Object rootObject, Object value);
}

{ .api }

CompositeStringExpression Class

public class CompositeStringExpression implements Expression {
    public CompositeStringExpression(String expressionString, Expression[] expressions);
    
    // Implements all Expression interface methods
    // Evaluates each sub-expression and concatenates results as strings
}

{ .api }

Expression Language Features

Basic Syntax Examples

ExpressionParser parser = new SpelExpressionParser();

// Literals
Expression exp = parser.parseExpression("'Hello World'");
String result = exp.getValue(String.class); // "Hello World"

exp = parser.parseExpression("42");
Integer number = exp.getValue(Integer.class); // 42

exp = parser.parseExpression("true");
Boolean bool = exp.getValue(Boolean.class); // true

// Mathematical operations
exp = parser.parseExpression("2 + 3 * 4");
Integer math = exp.getValue(Integer.class); // 14

exp = parser.parseExpression("10 / 3.0");
Double division = exp.getValue(Double.class); // 3.3333333333333335

// String operations
exp = parser.parseExpression("'Hello' + ' ' + 'World'");
String concat = exp.getValue(String.class); // "Hello World"

// Logical operations
exp = parser.parseExpression("true and false");
Boolean logical = exp.getValue(Boolean.class); // false

exp = parser.parseExpression("2 > 1 ? 'yes' : 'no'");
String conditional = exp.getValue(String.class); // "yes"

{ .api }

Property Access

public class Person {
    private String name;
    private int age;
    private Address address;
    // getters and setters...
}

public class Address {
    private String city;
    private String country;
    // getters and setters...
}

Person person = new Person();
person.setName("John");
person.setAge(30);

ExpressionParser parser = new SpelExpressionParser();

// Direct property access
Expression exp = parser.parseExpression("name");
String name = exp.getValue(person, String.class); // "John"

// Method invocation
exp = parser.parseExpression("name.toUpperCase()");
String upperName = exp.getValue(person, String.class); // "JOHN"

// Nested property access
exp = parser.parseExpression("address?.city");
String city = exp.getValue(person, String.class); // null (safe navigation)

// Property assignment (requires StandardEvaluationContext)
StandardEvaluationContext context = new StandardEvaluationContext(person);
exp = parser.parseExpression("name");
exp.setValue(context, "Jane");

{ .api }

Collection and Array Access

List<String> names = Arrays.asList("John", "Jane", "Bob");
Map<String, Integer> ages = Map.of("John", 30, "Jane", 25, "Bob", 35);
int[] numbers = {1, 2, 3, 4, 5};

ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
context.setVariable("names", names);
context.setVariable("ages", ages);
context.setVariable("numbers", numbers);

// List access
Expression exp = parser.parseExpression("#names[0]");
String firstName = exp.getValue(context, String.class); // "John"

// Map access
exp = parser.parseExpression("#ages['John']");
Integer age = exp.getValue(context, Integer.class); // 30

// Array access
exp = parser.parseExpression("#numbers[2]");
Integer number = exp.getValue(context, Integer.class); // 3

// Collection methods
exp = parser.parseExpression("#names.size()");
Integer size = exp.getValue(context, Integer.class); // 3

// Collection filtering and projection
exp = parser.parseExpression("#names.?[length() > 3]"); // Filter
List<String> filtered = (List<String>) exp.getValue(context); // ["John", "Jane"]

exp = parser.parseExpression("#names.![toUpperCase()]"); // Projection
List<String> projected = (List<String>) exp.getValue(context); // ["JOHN", "JANE", "BOB"]

{ .api }

Variables and Functions

StandardEvaluationContext context = new StandardEvaluationContext();

// Setting variables
context.setVariable("greeting", "Hello");
context.setVariable("name", "World");

// Using variables
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("#greeting + ' ' + #name");
String message = exp.getValue(context, String.class); // "Hello World"

// Registering functions
context.registerFunction("reverse", 
    String.class.getDeclaredMethod("valueOf", Object.class));

exp = parser.parseExpression("#reverse('hello')");
String reversed = exp.getValue(context, String.class);

// Built-in variables (when available)
exp = parser.parseExpression("#this"); // Current object
exp = parser.parseExpression("#root"); // Root object

{ .api }

Type References

ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();

// Type references
Expression exp = parser.parseExpression("T(java.lang.Math).PI");
Double pi = exp.getValue(context, Double.class); // 3.141592653589793

exp = parser.parseExpression("T(java.lang.Math).max(2, 3)");
Integer max = exp.getValue(context, Integer.class); // 3

// Constructor invocation
exp = parser.parseExpression("new java.util.Date()");
Date date = exp.getValue(context, Date.class);

exp = parser.parseExpression("new String('hello').toUpperCase()");
String upper = exp.getValue(context, String.class); // "HELLO"

{ .api }

Advanced Evaluation Patterns

Template Expressions

ExpressionParser parser = new SpelExpressionParser();
Map<String, Object> vars = new HashMap<>();
vars.put("name", "John");
vars.put("age", 30);

StandardEvaluationContext context = new StandardEvaluationContext();
context.setVariables(vars);

// Template with multiple expressions
String template = "Hello #{#name}, you are #{#age} years old and next year you'll be #{#age + 1}!";
Expression exp = parser.parseExpression(template, ParserContext.TEMPLATE_EXPRESSION);
String result = exp.getValue(context, String.class);
// "Hello John, you are 30 years old and next year you'll be 31!"

{ .api }

Safe Navigation

public class Customer {
    private Address address;
    // getters and setters...
}

Customer customer = new Customer(); // address is null

ExpressionParser parser = new SpelExpressionParser();

// Safe navigation operator (?.): prevents NullPointerException
Expression exp = parser.parseExpression("address?.city");
String city = exp.getValue(customer, String.class); // null (no exception)

// Without safe navigation (would throw NullPointerException)
// exp = parser.parseExpression("address.city"); // Throws exception

{ .api }

Elvis Operator

ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
context.setVariable("name", null);

// Elvis operator (?:): provides default value for null
Expression exp = parser.parseExpression("#name ?: 'Unknown'");
String name = exp.getValue(context, String.class); // "Unknown"

context.setVariable("name", "John");
name = exp.getValue(context, String.class); // "John"

{ .api }

Regular Expressions

ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
context.setVariable("text", "Hello World");

// Pattern matching
Expression exp = parser.parseExpression("#text matches '[A-Z].*'");
Boolean matches = exp.getValue(context, Boolean.class); // true

// Case-insensitive matching
exp = parser.parseExpression("#text matches '(?i)hello.*'");
matches = exp.getValue(context, Boolean.class); // true

{ .api }

Performance Optimization

Expression Compilation

// Enable compilation for better performance
SpelParserConfiguration config = new SpelParserConfiguration(
    SpelCompilerMode.IMMEDIATE, 
    Thread.currentThread().getContextClassLoader()
);
SpelExpressionParser parser = new SpelExpressionParser(config);

SpelExpression expression = parser.parseRaw("name.toUpperCase() + age.toString()");

// First evaluation interprets the expression
String result1 = expression.getValue(person, String.class);

// Subsequent evaluations use compiled bytecode (much faster)
String result2 = expression.getValue(person, String.class);

// Check if expression is compiled
boolean isCompiled = expression.compileExpression(); // true if successfully compiled

// Revert to interpreted mode if needed
expression.revertToInterpreted();

{ .api }

Expression Caching

// Cache frequently used expressions
public class ExpressionCache {
    private final Map<String, Expression> cache = new ConcurrentHashMap<>();
    private final ExpressionParser parser = new SpelExpressionParser();
    
    public Expression getExpression(String expressionString) {
        return cache.computeIfAbsent(expressionString, parser::parseExpression);
    }
}

// Usage
ExpressionCache cache = new ExpressionCache();
Expression exp = cache.getExpression("name.toUpperCase()");
String result = exp.getValue(person, String.class);

{ .api }

Best Practices

  1. Reuse Parsers: SpelExpressionParser is thread-safe and expensive to create
  2. Cache Expressions: Cache compiled expressions for repeated use
  3. Use Compilation: Enable compilation for frequently-evaluated expressions
  4. Choose Appropriate Context: Use SimpleEvaluationContext for better security and performance in data-binding scenarios
  5. Handle Exceptions: Always handle ParseException and EvaluationException appropriately
  6. Safe Navigation: Use safe navigation operator (?.) to avoid NullPointerException
  7. Type Safety: Specify expected result types when calling getValue()

Thread Safety Notes

  • SpelExpressionParser: Thread-safe, can be shared across threads
  • SpelExpression: Thread-safe for evaluation, but configuration methods should not be called concurrently
  • EvaluationContext: Generally not thread-safe, create separate instances per thread or evaluation
  • Compilation: Compiled expressions are thread-safe for evaluation

Install with Tessl CLI

npx tessl i tessl/maven-org-springframework--spring-expression

docs

advanced-features.md

error-handling.md

evaluation-contexts.md

expression-evaluation.md

index.md

method-constructor-resolution.md

property-index-access.md

spel-configuration.md

type-system-support.md

tile.json