Skip to content
JackSparrow414
Go back

SpEL: Getting Started and Quick Reference

Table of contents

Open Table of contents

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:

  1. ${} reads a property from a Spring Boot application*.yml configuration file.

    @Value("${jack.sparrow.name}")
    private Strinh
  2. #{} 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;

Notes

Example Code

Documentation


Share this post:

Previous Post
Getting Started with Java RMI
Next Post
Basic Ehcache Usage

Comments

Questions, corrections, and experiences are welcome. Sign in with GitHub to comment; both language versions share this discussion.

Comments are available on the live site only.