Spring Expression Language (SpEL)
Introduction
Spring Expression Language (SpEL) is a powerful expression language that supports querying and manipulating an object graph at runtime. It provides a way to configure bean properties dynamically and is widely used across different components of the Spring Framework.
SpEL was introduced in Spring 3.0 and has become an integral part of Spring, especially when working with configuration. It offers features similar to other expression languages like OGNL (Object Graph Navigation Language) or JSF EL (JavaServer Faces Expression Language), but is specifically tailored to work seamlessly with Spring's infrastructure.
Basic Syntax and Features
SpEL expressions are typically enclosed in #{}
(hash brackets) when used within Spring configuration files. This distinguishes them from property placeholders which use ${}
(dollar brackets).
Simple Expressions
Let's start with some basic expressions:
// Literal expressions
"#{'Hello World'}" // Evaluates to "Hello World"
"#{1 + 2}" // Evaluates to 3
// Property access
"#{person.name}" // Accesses the name property of person object
Expression Evaluation in Java Code
To use SpEL programmatically in your Java code:
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
public class SpELDemo {
public static void main(String[] args) {
ExpressionParser parser = new SpelExpressionParser();
// Evaluate a simple expression
Expression exp = parser.parseExpression("'Hello World'");
String message = (String) exp.getValue();
System.out.println(message); // Output: Hello World
// Arithmetic operations
exp = parser.parseExpression("10 * 5");
Integer result = (Integer) exp.getValue();
System.out.println(result); // Output: 50
}
}
SpEL in Spring Configuration
One of the most common uses of SpEL is within Spring configuration files to define bean properties dynamically.
XML Configuration Example
<bean id="mathCalculator" class="com.example.MathCalculator">
<property name="randomNumber" value="#{T(java.lang.Math).random() * 100.0}"/>
</bean>
Java Configuration Example
@Configuration
public class AppConfig {
@Bean
public MathCalculator mathCalculator() {
MathCalculator calculator = new MathCalculator();
calculator.setRandomNumber(Math.random() * 100.0);
return calculator;
}
}
Common SpEL Operators and Functions
Operators
SpEL supports various operators:
- Arithmetic:
+
,-
,*
,/
,%
,^
- Relational:
==
,!=
,<
,>
,<=
,>=
- Logical:
and
,or
,not
- Conditional:
?:
(ternary) - Regular Expression:
matches
Examples
// Arithmetic
parser.parseExpression("2 + 2").getValue(Integer.class); // 4
// Relational
parser.parseExpression("1 == 1").getValue(Boolean.class); // true
// Logical
parser.parseExpression("true and false").getValue(Boolean.class); // false
// Ternary
parser.parseExpression("'test' == 'test' ? 'yes' : 'no'").getValue(String.class); // "yes"
// Regular expression
parser.parseExpression("'5.00' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class); // true
Type References
SpEL allows you to reference static methods and fields using the T()
operator:
// Access static fields
parser.parseExpression("T(java.lang.Math).PI").getValue(Double.class); // 3.141592653589793
// Call static methods
parser.parseExpression("T(java.lang.Math).random()").getValue(Double.class); // Random value between 0.0 and 1.0
Working with Collections
SpEL provides powerful features for working with collections:
List Operations
// Create a list
Expression exp = parser.parseExpression("{'a', 'b', 'c', 'd'}");
List<String> list = (List<String>) exp.getValue();
// Output: [a, b, c, d]
// Access list elements (zero-based)
exp = parser.parseExpression("{'a', 'b', 'c', 'd'}[1]");
String item = (String) exp.getValue();
// Output: b
Map Operations
// Create a map
Expression exp = parser.parseExpression("{name:'John', age:25}");
Map<String, Object> map = (Map<String, Object>) exp.getValue();
// Output: {name=John, age=25}
// Access map elements
exp = parser.parseExpression("{name:'John', age:25}['name']");
String name = (String) exp.getValue();
// Output: John
Real-world Examples
Example 1: Conditional Bean Configuration
@Configuration
public class DatabaseConfig {
@Value("${app.environment}")
private String environment;
@Bean
public DataSource dataSource() {
DataSource ds = new BasicDataSource();
// Use SpEL to set different connection pools based on environment
ds.setMaxConnections(
environment.equals("production") ? 100 : 10
);
return ds;
}
}
Example 2: Using SpEL with Spring Security
SpEL is extensively used in Spring Security for method-level security:
@Service
public class UserService {
// Only allow access if the current user's username matches the requested username
@PreAuthorize("#username == authentication.principal.username")
public User loadUserDetails(String username) {
// Load and return user details
}
// Only administrators can delete users
@PreAuthorize("hasRole('ROLE_ADMIN')")
public void deleteUser(Long userId) {
// Delete user logic
}
}
Example 3: Dynamic Property Value in Spring Boot
@Component
public class EmailService {
// Get email content from application properties based on the locale
@Value("#{systemProperties['user.language'] == 'fr' ? '${email.greeting.french}' : '${email.greeting.english}'}")
private String emailGreeting;
public void sendWelcomeEmail(String recipient) {
// Use emailGreeting which was dynamically set based on system locale
System.out.println("Sending email to " + recipient + " with greeting: " + emailGreeting);
}
}
SpEL in Annotations
SpEL is widely used in various Spring annotations:
@Value Annotation
The @Value
annotation is one of the most common places where SpEL is used:
@Component
public class MathComponent {
// Inject a random number between 0 and 100
@Value("#{T(java.lang.Math).random() * 100.0}")
private double randomNumber;
// Inject a system property
@Value("#{systemProperties['user.name']}")
private String username;
// Combine property placeholder and SpEL
@Value("#{${server.max.threads} * 2}")
private int maxThreads;
}
@Cacheable Annotation
SpEL is also used in caching annotations to define dynamic keys:
@Service
public class ProductService {
@Cacheable(value = "products", key = "#id")
public Product getProductById(Long id) {
// Logic to fetch product from database
}
@Cacheable(value = "products", condition = "#price > 100")
public List<Product> getProductsByPrice(double price) {
// Only cache expensive products
}
}
Best Practices and Considerations
- Keep expressions simple: Complex expressions can be hard to read and maintain.
- Consider performance: Expression evaluation has overhead, so avoid using SpEL for performance-critical operations.
- Handle errors: Use proper error handling when evaluating expressions.
- Test thoroughly: Test SpEL expressions with different inputs to ensure they work correctly.
- Security considerations: Be cautious when using SpEL with user input to avoid security vulnerabilities.
Common Errors and Troubleshooting
Type Conversion Issues
// This might cause a ClassCastException if the expression doesn't evaluate to a String
String result = parser.parseExpression("1 + 2").getValue(String.class);
// Better approach
String result = parser.parseExpression("1 + 2").getValue().toString();
Null Safety
// This might cause a NullPointerException if person is null
parser.parseExpression("person.name").getValue(context);
// Safer approach using Elvis operator
parser.parseExpression("person?.name").getValue(context);
Summary
Spring Expression Language (SpEL) is a powerful feature of the Spring Framework that allows for dynamic expression evaluation at runtime. It provides a concise and flexible way to manipulate object graphs and supports a wide range of operations including arithmetic, logical, and relational operations, as well as method invocation and property access.
We've explored:
- Basic syntax and features of SpEL
- How to evaluate expressions programmatically
- Using SpEL in Spring configuration
- Working with collections and operators
- Real-world examples and common use cases
- Best practices and error handling
SpEL is used extensively throughout the Spring ecosystem, including Spring Core, Spring Security, and Spring Data, making it an essential skill for Spring developers.
Further Resources
- Official Spring Expression Language Documentation
- Spring Expression Language Guide on Baeldung
- SpEL GitHub Examples Repository
Exercises
- Create a Spring Boot application that uses SpEL to dynamically configure a bean's property based on the running environment (dev, test, prod).
- Write a SpEL expression to filter a list of products based on their price and category.
- Implement a custom method in your application that can be called from SpEL expressions.
- Create a Spring Security configuration that uses SpEL to implement complex access control rules.
- Use SpEL with Spring's caching annotations to implement a dynamic caching strategy.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)