September 7, 2022

Spring Expression Language (SpEL) Tutorial

The Spring Expression Language (SpEL) is an expression language that supports querying and manipulating an object graph at runtime. In this post we'll see examples of using SpEL with both XML and annotation based configuration.

The expression language supports the following functionality:

  • Literal expressions
  • Boolean, relational and mathematical operators
  • Regular expressions
  • Class expressions
  • Accessing properties, arrays, lists, and maps
  • Method invocation
  • Assignment
  • Calling constructors
  • Bean references
  • Inline lists
  • Inline maps
  • Ternary operator

SpEL expressions in Bean Definitions

Syntax to define the expression is of the form #{ <expression string> }. For example assigning a literal expression using SpEL.

If using XML configuration

<bean id="address" class="com.knpcode.springproject.Address">
  <property name="pinCode" value = "#{'110001'}" />
</bean>

If using @Value annotation

@Component
public class Address {
  ...
  ...
  @Value("#{'302001'}")
  private String pinCode;
  ...
  ...
}

SpEL Relational operators

SpEL supports the following relational operators. You can use the symbol or alphabetic equivalent (in case of XML document)

  • lt (<)
  • gt (>)
  • le (<=)
  • ge (>=)
  • eq (==)
  • ne (!=)
  • not (!)
SpEL Relational operators
@Component
public class Values {
  @Value("#{6 < 7}")
  private boolean valueLessThan;
  @Value("#{6 == 7}")
  private boolean valueEqual;
  @Value("#{6 == 7}")
  private boolean valueNotEqual;
  // Using alphabetic
  @Value("#{6 ge 7}")
  private boolean valueGreaterEqual;
  public boolean isValueLessThan() {
    return valueLessThan;
  }
  public boolean isValueEqual() {
    return valueEqual;
  }
  public boolean isValueNotEqual() {
    return valueNotEqual;
  }
  public boolean isValueGreaterEqual() {
    return valueGreaterEqual;
  }
}
Output
isValueLessThan()-true
isValueEqual()-false
isValueNotEqual()-false
isValueGreaterEqual()-false

SpEL Logical operators

SpEL supports the following logical operators. You can use the symbol or alphabetic equivalent.

  • and (&&)
  • or (||)
  • not (!)
SpEL Logical operators example
@Component
public class Values {
  @Value("#{6 < 7 && 7 < 8} ")
  private boolean valueLessThan;
  @Value("#{6 == 7 and 7 == 7}")
  private boolean valueEqual;
  @Value("#{6 != 7 || 8 != 8}")
  private boolean valueNotEqual;
  @Value("#{!(6 lt 7)}")
  private boolean valueNotOperator;
  public boolean isValueLessThan() {
    return valueLessThan;
  }
  public boolean isValueEqual() {
    return valueEqual;
  }
  public boolean isValueNotEqual() {
    return valueNotEqual;
  }
  public boolean isValueNotOperator() {
    return valueNotOperator;
  }
}
Output
isValueLessThan()-true
isValueEqual()-false
isValueNotEqual()-true
isValueNotOperator()-false

SpEL Mathematical operators

SpEL supports the following mathematical operators.

  • addition operator (+) can be used with both numbers and strings.
  • subtraction operator (-)
  • multiplication operator (*)
  • division operator (/)
  • modulus (%)
  • exponential power (^)

Subtraction, multiplication, and division operators can be used only on numbers.

SpEL Mathematical operators example
@Component
public class Values {
  @Value("#{6 + 8} ")
  private int valueAddition;
  @Value("#{'Hello ' + 'World'}")
  private String valueStringAddition;
  @Value("#{17 div 5}")
  private int valueDivision;
  @Value("#{17 % 5}")
  private int valueModDivision;
  @Value("#{2^3}")
  private double valuePower;
  public int getValueAddition() {
    return valueAddition;
  }
  public String getValueStringAddition() {
    return valueStringAddition;
  }
  public int getValueDivision() {
    return valueDivision;
  }
  public int getValueModDivision() {
    return valueModDivision;
  }
  public double getValuePower() {
    return valuePower;
  }
}
Output
valueAddition- 14
valueStringAddition- Hello World
valueDivision- 3
valueModDivision- 2
valuePower- 8.0

SpEL Bean reference and Method invocation example

In this example we’ll see how to reference a bean and how to invoke a method on a bean using Spring expression language. For that we’ll have two classes Person and Address and Person will have Address bean reference.

Address class
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class Address {
  @Value("100")
  private String houseNo;
  @Value("The Mall")
  private String street;
  @Value("Shimla")
  private String city;
  @Value("HP")
  private String state;
  // As SpEL literal
  @Value("#{'171004'}")
  private String pinCode;
  public String getHouseNo() {
    return houseNo;
  }
  public String getStreet() {
    return street;
  }
  public String getCity() {
    return city;
  }
  public String getState() {
    return state;
  }
  public String getPinCode() {
    return pinCode;
  }
  public void setHouseNo(String houseNo) {
    this.houseNo = houseNo;
  }
  public void setStreet(String street) {
    this.street = street;
  }
  public void setCity(String city) {
    this.city = city;
  }
  public void setState(String state) {
    this.state = state;
  }
  public void setPinCode(String pinCode) {
    this.pinCode = pinCode;
  }
}
Person class
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class Person {
  @Value("#{'Suresh'}")
  private String name;
  @Value("34")
  private int age;
  // SpEL Bean reference
  @Value("#{address}")
  private Address address;
  @Value("#{address.city}")
  private String personCity;
  // SpEL Method invocation 
  @Value("#{person.getInfo()}")
  private String personInfo;
  public String getName() {
    return name;
  }
  public int getAge() {
    return age;
  }
  public Address getAddress() {
    return address;
  }
  public String getPersonCity() {
    return personCity;
  }
  public String getPersonInfo() {
    return personInfo;
  }
  public void setName(String name) {
    this.name = name;
  }
  public void setAge(int age) {
    this.age = age;
  }
  public void setAddress(Address address) {
    this.address = address;
  }
  public void setPersonCity(String personCity) {
    this.personCity = personCity;
  }
  public void setPersonInfo(String personInfo) {
    this.personInfo = personInfo;
  }
	
  public String getInfo(){
    return getName() +   " - Address " + getAddress().getHouseNo() + 
        ", " + getAddress().getStreet() + ", " + getAddress().getCity() 
      + ", " + getAddress().getState() + ", " + getAddress().getPinCode();
  }	
}
Output
Person State HP
Person City Shimla
Person Info Suresh - Address 100, The Mall, Shimla, HP, 171004

XML configuration with SpEL expressions for the same beans can be done as given below, note that one difference is that getInfo() method is in Address bean.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans.xsd   
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">
    
  <bean id="address" class="com.knpcode.springexample.Address">
    <property name="houseNo" value = "100" />
    <property name="street" value = "The Mall" />
    <property name="city" value = "Shimla" />
    <property name="state" value = "HP" />
    <property name="pinCode" value = "#{'171004'}" />
  </bean>
    
  <bean id="person" class="com.knpcode.springexample.Person">
    <property name="name" value = "#{'Suresh'}" />
    <!--  Bean reference through SpEL -->
    <property name="address" value = "#{address}" />
    <property name="personCity" value = "#{address.city}" />
    <!--  SpEL Method invocation-->
    <property name="personInfo" value = "#{address.getInfo()}" />
  </bean>
</beans>

Using SpEL Safe Navigation Operator (?.)

The safe navigation operator is used to avoid a NullPointerException. Typically, when you have a reference to an object, you might need to verify that it is not null before accessing methods or properties of the object. The safe navigation operator returns null, if an object reference is null, instead of throwing an exception.

For example there is an Address class with properties as given below. Note that city doesn’t have any value.

@Component
public class Address {
  @Value("100")
  private String houseNo;
  @Value("The Mall")
  private String street;
  private String city;
  @Value("HP")
  private String state;
  // As SpEL literal
  @Value("#{'171004'}")
  private String pinCode;

Now you try to get the city in personCity property of the Person class and change it to all upper case.

@Component
public class Person {
  @Value("#{'Suresh'}")
  private String name;
  @Value("34")
  private int age;
  // SpEL Bean reference
  @Value("#{address}")
  private Address address;
  @Value("#{address.city.toUpperCase()}")
  private String personCity;

This assignment throws UnsatisfiedDependencyException as city itself is returned as null.

 org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'person': Unsatisfied dependency expressed through field 'personCity'; nested exception is org.springframework.beans.factory.BeanExpressionException: Expression parsing failed; nested exception is org.springframework.expression.spel.SpelEvaluationException: EL1011E: Method call: Attempted to call method toUpperCase() on null context object

To avoid the exception safe navigation operator (?.) can be used, now personCity itself is returned as null.

@Value("#{address.city?.toUpperCase()}")
private String personCity;

SpEL Ternary and Elvis operators

You can use the ternary operator for performing if-then-else conditional logic inside the expression.

@Component
public class Values {
  @Value("#{10} ")
  private int value1;
  @Value("#{20}")
  private int value2;
  @Value("#{30}")
  private int value3;
  public int getValue1() {
    return value1;
  }
  public int getValue2() {
    return value2;
  }
  public int getValue3() {
    return value3;
  }
  public void setValue1(int value1) {
    this.value1 = value1;
  }
  public void setValue2(int value2) {
    this.value2 = value2;
  }
  public void setValue3(int value3) {
    this.value3 = value3;
  }	
}

In the ConditionOps class value for the property ternaryValue is calculated using SpEL ternary operator. If value1 is greater than value3 then ternaryValue is equal to value1 otherwise it is equal to value3.

@Component
public class ConditionOps {
  @Value("#{values.value1 gt values.value3 ? values.value1 : values.value3 }")
  private int ternaryValue;

  public int getTernaryValue() {
    return ternaryValue;
  }

  public void setTernaryValue(int ternaryValue) {
    this.ternaryValue = ternaryValue;
  }
}
SpEL Elvis operator example

The Elvis operator is a shortening of the ternary operator syntax. With the ternary operator syntax, you usually have to repeat a variable twice when checking if the variable is null and then return the variable if not null or a default value if null.

@Value("#{values.name != null ? values.name : \"Unknown\"}")
private String displayName;

In such scenario you can use the Elvis operator-

@Value("#{values.name ?: \"Unknown\"}")
private String displayName;

SpEL with regular expression

SpEL uses matches operator with regular expression. The matches operator returns true if regular expression matches the given value otherwise false.

@Value("#{values.email matches '[a-zA-Z0-9._]+@[a-zA-Z0-9]+\\.com'}")
private boolean isEmail;

That's all for the topic Spring Expression Language (SpEL) Tutorial. If something is missing or you have something to share about the topic please write a comment.


You may also like

No comments:

Post a Comment