Table of contents
Open Table of contents
- SpEL Quick Reference
- Basics
- Reading Values
- Assigning Values
- Notes
SpEL Quick Reference
This post is a quick reference for SpEL expressions that come up in everyday development. More detailed examples are available in the example code.
Basics
The Two @Value Formats
In Spring Boot, @Value supports two forms:
-
${} reads a property from a Spring Boot application*.yml configuration file.
@Value("${jack.sparrow.name}") private Strinh -
#{} contains a SpEL expression.
@Value("#{spelEntity.name}") private String name;
The Role of the SpEL Context
Like other containers, it is responsible for resolving SpEL expressions.
With a rough understanding of these two basics, we can begin learning SpEL.
Reading Values
Reading values is common in everyday development, so let us start there. Think about the properties normally stored in a Java object and how to retrieve each of them. Studying those cases is a quick way to get started with SpEL.
Example Java Object
Create a Java object as described above. Its properties will typically be wrapper types, collections, or other Java objects—roughly these three categories.
@Getter
@Setter
@ToString
public class SpelEntity {
private Integer age = new Integer(2021);
private String name = "jacksparrow";
private Boolean child;
private List<String> list;
private Map<Integer, Integer> map;
private SpelAnotherEntity anotherEntity;
private List<SpelAnotherEntity> objectList;
private Map<String, SpelAnotherEntity> objectMap;
/**
* Instance method.
*/
public Date myDate() {
return Calendar.getInstance().getTime();
}
}
@Getter, @Setter, and @ToString are Lombok annotations. You can look into them if interested; I will not cover them in detail here.
Create an instance and assign some example property values. Use ExpressionParser to parse SpEL:
ExpressionParser parser = new SpelExpressionParser();
Reading Object Properties
log.info("name property value: {}", parser.parseExpression("name").getValue(spelEntity));
log.info("age property value: {}", parser.parseExpression("age").getValue(spelEntity));
log.info("child property value: {}", parser.parseExpression("child").getValue(spelEntity));
Retrieving Objects from Collections
// Access a list by index and a map by [key]; for a string key, use ['key']
log.info("First list value: {}", parser.parseExpression("list[0]").getValue(spelEntity));
log.info("Map value for key 1: {}", parser.parseExpression("map[1]").getValue(spelEntity));
Reading Properties of Objects in Collections
// list[index] retrieves the object as usual; access its properties with .property
log.info("Property of an object in a List: {}", parser.parseExpression("objectList[0].date").getValue(spelEntity));
// When a map value is an object, access its properties with .property as well
log.info("Property of an object in a Map: {}", parser.parseExpression("objectMap['objectKey'].date").getValue(spelEntity));
Retrieving an Object
Retrieve and return an object through a SpEL expression.
// Specify the return type in getValue when retrieving a property or object
SpelAnotherEntity spelAnotherEntity = parser.parseExpression("anotherEntity").getValue(spelEntity, SpelAnotherEntity.class);
log.info("Object property in spelEntity: {}", spelAnotherEntity);
// For a Map property, set the return type to Map and assign it to the appropriate Map<K, V>
Map<String, SpelAnotherEntity> objectMap1 = parser.parseExpression("objectMap").getValue(spelEntity, Map.class);
Retrieving an Object and Calling a Method
log.info("Result of toUpperCase: {}", parser.parseExpression("list[0].toUpperCase()").getValue(spelEntity));
You can also call methods outside the JDK, such as ones you write yourself. Here is an example using a constructor:
SpelAnotherEntity value1 = parser.parseExpression("new com.learn.spring.springuse.advanced.spel.SpelAnotherEntity()").getValue(spelEntity, SpelAnotherEntity.class);
Calling Static Utility Methods
The format is T(fully qualified class name).method(arguments).
log.info("Result of static method: {}", parser.parseExpression("T(org.springframework.util.StringUtils).capitalize(list[1])").getValue(spelEntity));
Less Common Features Worth Knowing
These features are less common in everyday development, but knowing about them does no harm.
Checking Collections for Missing Values
When retrieving a collection element or its properties, accessing a property of an element that does not exist produces an NPE. How can we handle this?
// Avoid errors when an element is missing or an index is too large using a ternary expression. To access element 4, return null if list.size() is less than 4
log.info("Checking list bounds: {}", parser.parseExpression("list.size() < 4 ? null:list[3]").getValue(spelEntity));
log.info("Accessing a missing map object property without an NPE (Null Pointer Exception): {}", parser.parseExpression("objectMap['objectKeys']?.date").getValue(spelEntity));
Filtering Collections
Filtering a list
// Select matching elements. The result is a collection: filtering a list returns a list; filtering a map returns a map
// Syntax: ?.[condition]
// #this refers to the current object
List<String> filterList = (List<String>) parser.parseExpression("list.?[#this =='china']").getValue(spelEntity);
log.info("Filtered list value: {}", filterList.get(0));
// Selection returns all matching elements; to retrieve the first or last matching element:
// First: .^[condition]
// Last: .$[condition]
log.info("First matching list value: {}", parser.parseExpression("list.^[#this == 'china']").getValue(spelEntity));
log.info("First matching list value: {}", parser.parseExpression("list.$[#this == 'china']").getValue(spelEntity));
Filtering a map
Map<Integer, Integer> filterKeyMap = (Map<Integer, Integer>) parser.parseExpression("map.?[key == 1]").getValue(spelEntity);
Map<Integer, Integer> filterValueMap = (Map<Integer, Integer>) parser.parseExpression("map.?[value == 100]").getValue(spelEntity);
log.info("Map filtered by key: {}", filterKeyMap);
log.info("Map filtered by value: {}", filterValueMap);
Assigning Values
Primitive and Wrapper Types
For these types, assign the value directly in @Value(”#{SpEL expression}”).
@Value("#{1}")
private Integer code;
@Value("#{'initString'}")
private String initStr;
@Value("#{true}")
private Boolean initBool;
Assigning Collections
Use {} to represent a collection. For a list, place the element values directly inside {}; for a Map, use key:value entries. Separate elements with commas.
@Value("#{ {'jack', 'jan'} }")
private List<String> integerList;
@Value("#{ {1, 2,3} }")
private List<Integer> stringList;
@Value("#{ {'china':1, 'usa':2, 'japan':3} }")
private Map<String, Integer> map;
Calling Methods for Properties
This works similarly to reading values; here is a simple example:
// Assign an object-valued property by calling its constructor
@Value("#{ new com.learn.spring.springuse.advanced.spel.SpelEntity() }")
private SpelEntity spelEntity;
// Call a static method
@Value("#{ T(com.google.common.collect.Lists).newArrayList() }")
private List<Integer> initListByGuava;