Learning the Spring Expression Language (SpEL)
The Spring Expression language (SpEL) is a powerful tool for manipulating objects at runtime. Check out this tutorial to learn how to use the Spring Expression Language.
Join the DZone community and get the full member experience.
Join For FreeOverview
The Spring Expression Language (abbreviated as SpEL) is a powerful expression language. Within the Spring portfolio, it serves as the foundation for expression evaluation. It supports querying and manipulating an object graph at runtime. It can be used with both XML-based and annotation-based Spring configurations and bean definitions. Since it is capable of assigning value dynamically at runtime, it can save us a lot of code.
Project Setup
For a Maven project, the following dependencies should be used:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>com.sun.mail</groupId>
<artifactId>javax.mail</artifactId>
<version>1.6.0</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
</dependencies>
The first two dependencies, spring-core
and spring-context
are required for SpEL. The other two dependencies, javax.mail
and commons-io,
will be used in a practical example of SpEL.
Language Syntax and Features
SpEL supports standard mathematical operators, relational operators, logical operators, conditional operators, collections and regular expressions, etc. It can be used to inject a bean or a bean property into another bean. Method invocation of a bean is also supported. Here are some basic features and operators of SpEL:
The literal expression can be used in SpEL expression. For example, "Hello SpEL" is a String literal. If this literal is used as a SpEL expression, the evaluated value will also be "Hello SpEL."
Method invocation is supported in the SpEL expression. For example, the concat method can be called from a String literal.
The mathematical operators are supported in SpEL expression. All basic operators, like addition (+), subtraction (-), multiplication (*), division (/), modulus (%), exponential power (^), etc., can be used in a SpEL expression.
The relational operators equal (==), not equal (!=), less than (<), less than or equal (<=), greater than (>), and greater than or equal (>=) are supported in a SpEL expression, as well. To use the relational operator in the XML based configuration, the textual equivalents eq, ne, lt, le, gt, ge should be used instead.
The logical operators, (&&) or (||) and not (!), are supported. The textual equivalents can also be used.
The ternary operator is used for performing if-then-else conditional logic inside the SpEL expression. It is useful when we need to inject a value based on some condition.
The Elvis operator is a shortening form of the ternary operator. One common use of the ternary operator is the null checking of a variable and then returning the variable value or a default value. The Elvis operator is the shortcut way of doing the job.
The use of a regular expression is supported in this SpEL expression. We need to use the matches operator to check whether a string matches a given regular expression.
We will use the SpelExpressionParser
, which is an implementation of the ExpressionParser
interface to parse the SpEL expressions. Calling the parseExpression
method of the SpelExpressionParser
will return an instance of SpelExpression
, which is an implementation of the Expression interface. The evaluated result can be found by calling the getValue
method. Here are code examples of the above features of the SpEL expression.
public class ExpressionParserExample1 {
public static void main(String[] args) {
ExpressionParser expressionParser = new SpelExpressionParser();
// 1. Literal expression
Expression expression = expressionParser.parseExpression("'Hello SpEL'");
String strVal = expression.getValue(String.class);
System.out.println("1. Literal expression value:\n" + strVal);
// 2. Method invocation
expression = expressionParser.parseExpression("'Hello SpEL'.concat('!')");
strVal = expression.getValue(String.class);
System.out.println("2. Method invocation value:\n" + strVal);
// 3. Mathematical operator
expression = expressionParser.parseExpression("16 * 5");
Integer intVal = expression.getValue(Integer.class);
System.out.println("3. Mathematical operator value:\n" + intVal);
// 4. Relational operator
expression = expressionParser.parseExpression("5 < 9");
boolean boolVal = expression.getValue(Boolean.class);
System.out.println("4. Mathematical operator value:\n" + boolVal);
// 5. Logical operator
expression = expressionParser.parseExpression("400 > 200 && 200 < 500");
boolVal = expression.getValue(Boolean.class);
System.out.println("5. Logical operator value:\n" + boolVal);
// 6. Ternary operator
expression = expressionParser.parseExpression("'some value' != null ? 'some value' : 'default'");
strVal = expression.getValue(String.class);
System.out.println("6. Ternary operator value:\n" + strVal);
// 7. Elvis operator
expression = expressionParser.parseExpression("'some value' ?: 'default'");
strVal = expression.getValue(String.class);
System.out.println("7. Elvis operator value:\n" + strVal);
// 8. Regex/matches operator
expression = expressionParser.parseExpression("'UPPERCASE STRING' matches '[A-Z\\s]+'");
boolVal = expression.getValue(Boolean.class);
System.out.println("8. Regex/matches operator value:\n" + boolVal);
}
}
Now, we have evaluated the SpEL expression using the default context. SpEL expressions can be evaluated against a specific object instance, which is often mentioned as the root object. Let’s define a Bean
and use it as the context of the evaluation.
@Component("sampleBean")
public class SampleBean {
private String property = "String property";
private ArrayList<Integer> arrayList = new ArrayList<Integer>();
private HashMap<String, String> hashMap = new HashMap<String, String>();
public SampleBean() {
arrayList.add(36);
arrayList.add(45);
arrayList.add(98);
hashMap.put("key 1", "value 1");
hashMap.put("key 2", "value 2");
hashMap.put("key 3", "value 3");
}
public String getProperty() {
return property;
}
public void setProperty(String property) {
this.property = property;
}
public ArrayList<Integer> getArrayList() {
return arrayList;
}
public void setArrayList(ArrayList<Integer> arrayList) {
this.arrayList = arrayList;
}
public HashMap<String, String> getHashMap() {
return hashMap;
}
public void setHashMap(HashMap<String, String> hashMap) {
this.hashMap = hashMap;
}
}
We will create the evaluation context by creating an instance of StandardEvaluationContext
. It takes the root object ( SampleBean
in our case) as a parameter in it’s constructor. One thing to remember is that the creation of the StandardEvaluationContext
instance is expensive. So, we should cache and reuse them as much as possible. Here are some uses for evaluating the SpEL expression against the root object:
We can access the value of properties of a bean.
We can compare the value of a property of a bean with some specific value.
We can access the contents of the List property of a bean. The items of a List can be accessed by using the square bracket notation. The index of the item to be provided within the brackets.
We can access the contents of the map property of a bean. The contents of a map can also be accessed by using the square bracket notation. The key value has to be provided within the brackets.
Here are code examples of the above features.
public class ExpressionParserExample2 {
public static void main(String[] args) {
ExpressionParser expressionParser = new SpelExpressionParser();
// create EvaluationContext from bean
SampleBean contextBean = new SampleBean();
StandardEvaluationContext testContext = new StandardEvaluationContext(contextBean);
// 9. Property value
Expression expression = expressionParser.parseExpression("property");
String strVal = expression.getValue(testContext, String.class);
System.out.println("9. Property value:\n" + strVal);
// 10. Compare property
expression = expressionParser.parseExpression("property == 'String property'");
boolean boolVal = expression.getValue(testContext, Boolean.class);
System.out.println("10. Compare property:\n" + boolVal);
// 11. List value
expression = expressionParser.parseExpression("arrayList[0]");
strVal = expression.getValue(testContext, String.class);
System.out.println("11. List value:\n" + strVal);
// 12. Map value
expression = expressionParser.parseExpression("hashMap['key 1']");
strVal = expression.getValue(testContext, String.class);
System.out.println("12. Map value:\n" + strVal);
}
}
SpEL in Bean Definition
SpEL expressions can be used in bean definitions. It can be used with both XML-based and annotation-based configuration. A SpEL expression starts with a hash (#) symbol and is wrapped with braces. Thus, it follows the form #{ <expression string>}. SpEL expressions can be used to refer a bean or properties/methods of a bean. Here is an example of annotation-based configuration:
@Component("user")
public class User {
@Value("598")
private Integer id;
@Value("John")
private String firstName;
@Value("Doe")
private String lastName;
@Value("#{user.firstName.concat(' ').concat(user.lastName)}")
private String fullName;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getFullName() {
return fullName;
}
public void setFullName(String fullName) {
this.fullName = fullName;
}
}
Practical Example
We have learned the basic features of SpEL expressions. Now, let’s apply it in an interesting practical example. Suppose we want to send an HTML email to users. Some values should be injected into the HTML template dynamically in runtime. Using SpEL expressions in the HTML template can be a good solution in this case. Here is an example of an HTML template that contains SpEL expressions:
<html>
<head>
<title>HTML Email with SPEL expression</title>
</head>
<body>
<h4>Dear #{user.fullName},</h4>
<div style="color:blue;"><i>Thanks for registering to our system.</i></div>
<p>Best regards,
<br>#{company.getName()}
</p>
</body>
</html>
In the above example, we have used the User
bean inside the SpEL expression, which was defined earlier. We have also used the Company
bean in the SpEL expression. Here is the definition of a Company
bean:
@Component("company")
public class Company {
@Value("256")
private Integer id;
@Value("XYZ Inc.")
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
How will we inject value inside HTML template now? We have to iterate the content of the HTML template and find out all the SpEL expressions used in it. Each SpEL expression has to be parsed and evaluated. We have to replace all the SpEL expressions with the evaluated values in the content of HTML template. As the root object, we have to use the BeanExpressionContext
here. Our ApplicationContext's
BeanFactory
will be used to create the instance of BeanExpressionContext
.
For sending emails, we will use the Standard JavaMail API. As the SMTP server, we will use Yahoo's SMTP (we need to create an app password in case of Yahoo). Other SMTP servers can also be used in the same way. Here is the complete example:
public class HTMLEmailTest {
public String parseEmailContent(StringWriter emailTemplateContent) {
// populate spel context
ConfigurableApplicationContext appContext = new ClassPathXmlApplicationContext("applicationContext.xml");
StandardEvaluationContext spelContext = new StandardEvaluationContext();
spelContext.setBeanResolver(new BeanFactoryResolver(appContext.getBeanFactory()));
spelContext.addPropertyAccessor(new BeanExpressionContextAccessor());
BeanExpressionContext rootObject = new BeanExpressionContext(appContext.getBeanFactory(), null);
// create spel expression parser instance
ExpressionParser parser = new SpelExpressionParser();
// search the fileContent string and find spel expressions,
// then evaluate the expressions
Integer start = 0, braketStart = 0;
StringBuffer sb = emailTemplateContent.getBuffer();
while ((braketStart = sb.indexOf("#{", start)) > -1) {
Integer braketClose = sb.indexOf("}", start);
String expressionStr = sb.substring(braketStart + 2, braketClose);
Expression expression = parser.parseExpression(expressionStr);
String evaluatedValue = expression.getValue(spelContext, rootObject, String.class);
sb.replace(braketStart, braketClose + 1, evaluatedValue);
start = braketClose + evaluatedValue.length();
}
System.out.println(sb.toString());
return sb.toString();
}
public Properties getSmtpProperties() {
Properties props = new Properties();
props.put("mail.smtp.host", "smtp.mail.yahoo.com");
props.put("mail.smtp.auth", "true");
props.put("mail.debug", "true");
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.smtp.port", "465");
props.put("mail.smtp.socketFactory.port", "465");
props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
props.put("mail.smtp.socketFactory.fallback", "false");
return props;
}
public Session getMailSession(Properties props) {
Session mailSession = Session.getInstance(props, new javax.mail.Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication("XXXXXX@yahoo.com", "xxxxxxxxxxx");
}
});
mailSession.setDebug(true);
return mailSession;
}
public void populateAndSendEmail(Session mailSession, String emailBody) {
try {
Message msg = new MimeMessage(mailSession);
msg.setFrom(new InternetAddress("XXXXXX@yahoo.com"));
msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse("YYYYYY@gmail.com"));
msg.setSentDate(new Date());
msg.setSubject("HTML Email with SPEL expression");
msg.setContent(emailBody, "text/html");
// Send the email using Transport
Transport.send(msg);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
try {
HTMLEmailTest htmlEmailTest = new HTMLEmailTest();
String emailTemplate = "emailTemplate.html";
ClassLoader classLoader = htmlEmailTest.getClass().getClassLoader();
File file = new File(classLoader.getResource(emailTemplate).getFile());
StringWriter emailTemplateContent = new StringWriter();
IOUtils.copy(new FileInputStream(new File(file.getAbsolutePath())), emailTemplateContent);
// replace the SPEL expressions of email template with evaluated values
String emailBody = htmlEmailTest.parseEmailContent(emailTemplateContent);
// populate SMTP server properties
Properties props = htmlEmailTest.getSmtpProperties();
// generate email session
Session mailSession = htmlEmailTest.getMailSession(props);
// generate email message and send email
htmlEmailTest.populateAndSendEmail(mailSession, emailBody);
} catch (Exception e) {
e.printStackTrace();
}
}
}
The above application will send an HTML email with the body as shown below:
All the code examples in this article can be found here: https://github.com/ShahMinulAmin/spel.
Conclusion
In this article, we have learned about the basic features and syntax of the Spring expression language with some small examples. This one is a powerful feature of the Spring framework. It can be applied to various areas of any enterprise application developed by the Spring framework.
Opinions expressed by DZone contributors are their own.
Comments