Introduction
The article Introduction to Spring Expression Language (SpEL) will provide introductory details in writing expression languages using Spring’s Expression framework. The reader is expected to having a basic understanding on the core concepts of Spring before reading this article (Read: Introduction to Spring Framework). This article explains the usage of Spring’s Expression API for writing and parsing expressions. This article includes plenty of code samples for illustrating the concepts of expressions by explaining how to write custom functions and custom bean resolvers, accesing java methods etc. Later on, the article provides details about writing expressions pertaining to collections and modifying variables. The miscellaneous section of the article explains the usage of T operator and null-safe operator.
also read:
What is Spring Expression Language (SpEL)?
SpEL is powerful expression language created by Spring framework community to work with all the Spring framework products. There are several existing expression languages like MVEL, OGNL and JBoss EL. Every EL is developed with some purpose in mind. SpEL is created to help the Spring porfolio products. The syntax used for the SpEL is same like Unified EL. But, it has lot of powerful features that are missing in the Unified EL.
Another advantage of SpEL is it can work independently without depending on the Spring environment. In the following sections, you will read the complete details on how tto write and use the Spring Expression Language.
Writing Spring Expressions
In this section we will write a simple program which illustrates the usage of Spring Expression. Refer the sample code given below. To start with, we have constructed an expression parser object capable of parsing expressions fed into it. One concrete expression parser object that comes with Spring Expression is the SpelExpressionParser class which stands for Spring Expression Language Parser. An instance of Expression object can be obtained by calling the parseExpression() method which is defined on the Parser object.
package net.javabeat.articles.spring3.el; import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; public class ExpressionTest { public static void main(String[] args) { ExpressionParser parser = new SpelExpressionParser(); Expression expression = parser.parseExpression("'Testing Spring Expression Framework'"); String message = (String) expression.getValue(); System.out.println("Message is " + message); } }
The value passed to this method must be a valid expression string. During the course of this article, we will cover in-depth details with respect to the various applicable syntax that can be passed to this method. A call to getValue() defined on the Expression object will resolve and return the desired result. In the above example, the expression passed is expected to return an instance of String.
Using user-defined functions in Expressions
In the last section, we saw how to write a simple program using Spring’s Expression API. In this example, we will see two more new concepts. The first one is how to make use of Spring’s EvaluationContext which serves as a context object for storing various objects and that can be later used while parsing expressions. The second is to write Custom functions and to register it so that such functions can be used as part of expressions.
Have a look at the below utility method which finds out the maximum elements from a collection and returns it. Later on in this section, we will use this class for illustration purpose.
package net.javabeat.articles.spring3.el.utils; import java.util.Collection; import java.util.Iterator; public class CollectionUtils { public static Integer maxElement(Collection collection){ Integer maxElement = null; Iterator iterator = collection.iterator(); while (iterator.hasNext()){ Integer integer = iterator.next(); if (maxElement == null){ maxElement = integer; }else{ if (integer.intValue() > maxElement.intValue()){ maxElement = integer; } } } return maxElement; } }
One more utility class which determines if a given number is prime or not is given below.
package net.javabeat.articles.spring3.el.utils; public class MathUtils { public static boolean isPrime(Integer number){ if (number == 0){ return false; } for (int index = 2; index < number; index ++){ if (number %index == 0){ return false; }else{ continue; } } return true; } }
In the below code, we have initialized an instance of EvaluationContext object whose concrete implementation happens to be the class StandardEvaluationContext. A context evaluation object can be used for storing any number of objects which can be used later in the expressions for retrieving the values. We will see examples for this scenario later in this article. Other than this usage, an evaluation context can also be used for registering user-defined functions which can later be referenced in functions.
package net.javabeat.articles.spring3.el; import java.lang.reflect.Method; import java.util.Collection; import net.javabeat.articles.spring3.el.utils.CollectionUtils; import net.javabeat.articles.spring3.el.utils.MathUtils; import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; public class FunctionsTest { public static void main(String[] args) throws Exception{ ExpressionParser parser = new SpelExpressionParser(); StandardEvaluationContext context = new StandardEvaluationContext(); Method method = null; Expression expression = null; Boolean value = null; // Register the method isPrime() method = MathUtils.class.getMethod("isPrime", Integer.class); context.registerFunction("prime", method); expression = parser.parseExpression("#prime(10)"); value = expression.getValue(context, Boolean.class); System.out.println("Number 10 is prime: " + value); expression = parser.parseExpression("#prime(37)"); value = expression.getValue(context, Boolean.class); System.out.println("Number 37 is prime: " + value); // Register the method maxElement() method = CollectionUtils.class.getMethod("maxElement", Collection.class); context.registerFunction("max", method); expression = parser.parseExpression("#max({10, 43, 45, 98, 32, 1})"); Integer maxElement = expression.getValue(context, Integer.class); System.out.println("Max element in the list is : " + maxElement); } }
The argument to the registerFunction() method which can be used for registering user-defined function is the name of the function and a method object. For example for registering the MathUtils.isPrime() method we have obtained an instance of the method object by calling the standard reflection APIs. Note that it is not necessary that the Java method name must match the name of the function that is passed to the registerFunction() method. In this case, even though the java method name is isPrime(), this method is registered and available under the name ‘prime’. Once the method is registered, it can be used in expressions as ‘#functionName(arguments)’. Note that the method MathUtils.isPrime() takes one argument and hence the expression language used for accessing the function is ‘#prime(10)’.
Similarly the java method CollectionUtils.maxElement() is registered under the name ‘max’. Note that this method accepts a collection object as its argument and to suit this we have created a list object using the list ‘{element1, element2}’.
Accessing Methods in Spring Expression
In this section, we will see how to access methods defined in Java language. Have a look at the below code.
package net.javabeat.articles.spring3.el; import java.util.List; import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; public class MethodAccessTest { public static void main(String[] args) { test1(); test2(); test3(); } private static void test1(){ ExpressionParser parser = new SpelExpressionParser(); Expression expression = parser.parseExpression("new java.lang.String('Hello')"); String stringObject = expression.getValue(String.class); System.out.println("String object is " + stringObject); } private static void test2(){ ExpressionParser parser = new SpelExpressionParser(); Expression expression = parser.parseExpression("'Find me'.charAt(3)"); Character character = expression.getValue(Character.class); System.out.println("Character at position 3 is " + character); } private static void test3(){ ExpressionParser parser = new SpelExpressionParser(); Expression expression = parser.parseExpression("new java.util.ArrayList()"); @SuppressWarnings("unchecked") List numbers = expression.getValue(List.class); System.out.println("Numbers list is " + numbers); } }
In the first test method, we have used ‘new ClassName()’ for creating an instance of the class and the same is later referenced in the getValue() object. Note that getValue() method is overloaded, if the caller for sure knows the return type of the evaluated expression, the the overloaded version getVerion(Class desiredReturnType) can be used. One other way for accessing string objects is to directly refer them as literals in the expressions. For example in the second test method a string literal is defined and the method ‘charAt()’ is directly defined on the string literal object.
Resolving Bean objects
We have seen in the last few sections that an evaluation context object can be used for storing objects as well as for registering user-defined objects. Other use cases for defining a evaluation context object is to define a custom bean resolver object for resolving bean references.
package net.javabeat.articles.spring3.el; import net.javabeat.articles.spring3.el.utils.SimpleBeanResolver; import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; public class BeanReferencingTest { public static void main(String[] args) { ExpressionParser parser = new SpelExpressionParser(); StandardEvaluationContext context = new StandardEvaluationContext(); SimpleBeanResolver beanResolver = new SimpleBeanResolver(); context.setBeanResolver(beanResolver); Expression expression = null; expression = parser.parseExpression("@string"); String strResult = expression.getValue(context, String.class); System.out.println("Result is " + strResult); expression = parser.parseExpression("@float"); Float floatResult = expression.getValue(context, Float.class); System.out.println("Result is " + floatResult); } }
Bean objects can be referenced in expressions with the syntax ‘@BeanName’. Such expressions that involve defining bean names have to resolved by attaching a custom bean resolver in the evaluation context object. In the above code sample, we have defined a custom bean resolver object by calling the method ‘setBeanResolver()’ method. Note that we have kept the implementation of the custom bean resolver as simple as possible by returning a pre-defined well object java object after checking the name of the bean.
package net.javabeat.articles.spring3.el.utils; import org.springframework.expression.AccessException; import org.springframework.expression.BeanResolver; import org.springframework.expression.EvaluationContext; public class SimpleBeanResolver implements BeanResolver { @Override public Object resolve(EvaluationContext context, String param) throws AccessException { if (param.equals("string")){ return new String("TEST"); }else if (param.equals("integer")){ return new Integer(100); }else if (param.equals("float")){ return new Float(10.34); }else{ return null; } } }
The above implementation checks for the parameter name and returns the java object accordingly. Hence, when the expression ‘@string’ is used, the second parameter will hold the bean name which is ‘string’ and accordingly an appropriate object type is returned to the caller.
How to use Collections in Spring Expression Language(SpEL)?
We will devote this section in accessing and manipulating collection objects in expression. For better illustration purposes, we will define various model objects in the below section.
The model class representing ‘Address’ is given below. The definition contains two properties namely ‘address’ and the ‘addressType’. The property ‘address’ is expected to hold the entire address contents and the property ‘addressType’ is an enumeration which will state the type of the address.
package net.javabeat.articles.spring3.el.model; public class Address { private String address; private AddressType addressType; public Address(String address, AddressType addressType){ this.address = address; this.addressType = addressType; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public AddressType getAddressType() { return addressType; } public void setAddressType(AddressType addressType) { this.addressType = addressType; } }
The enumeration ‘AddressType’ which denotes the type of address is given below. We have kept the definition as simple as possible by defining two address types namely ‘HOME’ and ‘BILLING’.
package net.javabeat.articles.spring3.el.model; public enum AddressType { HOME, BILLING }
The Customer model which makes use of the Address model is given below. Note that it is possible for a customer to own multiple addresses; hence the Customer class has the property ‘addresses’ which is of type List. Other than this, it holds two more properties, one is the identifier and the other is the name of the Customer.
package net.javabeat.articles.spring3.el.model; import java.util.ArrayList; import java.util.List; public class Customer { private String id; private String name; private Listaddresses; public Customer(String id, String name){ this.id = id; this.name = name; this.addresses = new ArrayList (); } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public List getAddresses() { return addresses; } public void setAddresses(List addresses) { this.addresses = addresses; } }
In the following section, we will see the list of model objects which are associated with a defect tracking system. A defect tracking system will have a set of defects. Each defect will have an assignee who is expected to fix the defect and that is modeled through the ‘Assignee’ object. A defect is usually associated with a release that is represented through ‘Release’ object.
The definition for the ‘Assignee’ class is given below. This class has one property ‘name’ which represents the name of the assignee.
package net.javabeat.articles.spring3.el.model; public class Assignee { private String name; public Assignee(String name){ this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public boolean equals(Object object){ if (!(object instanceof Assignee)){ return false; } Assignee incomingObject = (Assignee)object; return incomingObject.name.equals(name); } public int hashCode(){ return 1212 + name.hashCode(); } public String toString(){ return name; } }
A defect is often associated to a release and the definition of the ‘Release’ class is given below. Note that this class has two properties – the first being the ‘name’ which is the name of the release such as ‘Java/1.5’ or ‘Java/1.6’ and the second property ‘releaseDate’ represents the date when the release is available and given to its customers.
package net.javabeat.articles.spring3.el.model; import java.util.Date; public class Release { private String name; private Date releaseDate; public Release(String name){ this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Date getReleaseDate() { return releaseDate; } public void setReleaseDate(Date releaseDate) { this.releaseDate = releaseDate; } public boolean equals(Object object){ if (!(object instanceof Release)){ return false; } Release incomingRelease = (Release)object; return name.equals(incomingRelease.name); } public int hashCode(){ return 1212 + name.hashCode(); } public String toString(){ return name; } }
Here comes the definition of the Defect class which holds an identifier, the name of the defect. This class also holds two more complex properties ‘assignee’ and ‘forWhichRelease’ for which the description was elaborated in the last section.
package net.javabeat.articles.spring3.el.model; public class Defect { private String id; private String description; private Assignee assignee; private Release forWhichRelease; public Defect(String id, String description, Assignee assignee, Release forWhichRelease){ this.id = id; this.description = description; this.assignee = assignee; this.forWhichRelease = forWhichRelease; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public Assignee getAssignee() { return assignee; } public void setAssignee(Assignee assignee) { this.assignee = assignee; } public Release getForWhichRelease() { return forWhichRelease; } public void setForWhichRelease(Release forWhichRelease) { this.forWhichRelease = forWhichRelease; } public boolean equals(Object object){ if (!(object instanceof Defect)){ return false; } Defect incomingDefect = (Defect)object; return incomingDefect.id == id; } public int hashCode(){ return 133232 + id.hashCode(); } public String toString(){ return id + "-" + description + "-" + assignee + "-" + forWhichRelease; } }
The final model class in this section in the ‘DTSystem’ which stands for ‘Defect Tracking System’. This class holds a reference to the list of defects available in the system.
package net.javabeat.articles.spring3.el.model; import java.util.ArrayList; import java.util.List; public class DTSystem { private List defects; public DTSystem(){ defects = new ArrayList(); } public void addDefect(Defect defect){ defects.add(defect); } public List getDefects() { return defects; } public void setDefects(List defects) { this.defects = defects; } }
We will see the usage of collections in the following section. Have a look at the method ‘testCollections’. For illustration purpose, we have created an instance of Customer object and populated it with the address objects. To make the evaluation context object visible to the Customer object, the constructor of StandardEvaluationContext is passed on with the Customer object. Now it is possible to access the properties of the Customer object. An expression of ‘name’ will now retrieve the name value of the Customer object (by calling Customer.getName()). It is also possible to access the complex properties available in the customer object. For example, the property ‘addresses’ refer to a List object which is of type ‘Address’. Hence the expression ‘addresses[1].addressType’ will refer to the second object in the ‘addresses’ collection and then its ‘addressType’ property which is an enumeration. Since we know that the return type of this expression is an enumeration, we have used the overloaded version getValue() method without doing an explicit type-cast.
Have a look at the test method ‘testMap()’ for accessing Map objects. We have created a Map object by populating it with test customer objects. The key from the map object can be accessed using the syntax [‘keyName’]. For example, the expression ‘[‘DVD’]’ will retrieve the object with the key ‘DVD’ which will return ‘David’ customer object. Also the expression ‘[‘JHN’].addresses[0].address.length()’ will return the length of the address string which is retrieved from the first address object for the customer ‘David’.
package net.javabeat.articles.spring3.el; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import net.javabeat.articles.spring3.el.model.Address; import net.javabeat.articles.spring3.el.model.AddressType; import net.javabeat.articles.spring3.el.model.Assignee; import net.javabeat.articles.spring3.el.model.Customer; import net.javabeat.articles.spring3.el.model.DTSystem; import net.javabeat.articles.spring3.el.model.Defect; import net.javabeat.articles.spring3.el.model.Release; import org.springframework.expression.EvaluationContext; import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; public class CollectionsTest { public static void main(String[] args) { testCollections(); testMap(); testSelection(); testProjection(); } private static void testCollections(){ ExpressionParser parser = new SpelExpressionParser(); Customer customer = new Customer("JHN", "John"); Address homeAddress = new Address("Home Address", AddressType.HOME); Address billingAddress = new Address("Billing Address", AddressType.BILLING); customer.getAddresses().add(homeAddress); customer.getAddresses().add(billingAddress); EvaluationContext context = new StandardEvaluationContext(customer); Expression expression = null; expression = parser.parseExpression("name"); String customerName = expression.getValue(context, String.class); System.out.println("Customer name is " + customerName); expression = parser.parseExpression("addresses[1].addressType"); AddressType addressType = expression.getValue(context, AddressType.class); System.out.println("Address Type is " + addressType); } private static void testMap(){ Map<String, Customer> customersMap = new HashMap<String, Customer>(); Customer customer = null; customer = new Customer("JHN", "John"); customersMap.put("JHN", customer); customer.getAddresses().add(new Address("Billing Address", AddressType.BILLING)); customer = new Customer("JOH", "Johan"); customersMap.put("JOH", customer); customer = new Customer("DVD", "David"); customersMap.put("DVD", customer); EvaluationContext context = new StandardEvaluationContext(customersMap); ExpressionParser parser = new SpelExpressionParser(); Expression expression = null; expression = parser.parseExpression("['DVD'].name"); String customerName = expression.getValue(context, String.class); System.out.println("Customer name is " + customerName); expression = parser.parseExpression("['JHN'].addresses[0].address.length()"); Integer addressLength = expression.getValue(context, Integer.class); System.out.println("Address length is " + addressLength); } @SuppressWarnings("unchecked") private static void testSelection(){ ExpressionParser parser = new SpelExpressionParser(); DTSystem system = getDTSystem(); EvaluationContext context = new StandardEvaluationContext(system); Expression expression = null; String expressionStr = "Defects.?[forWhichRelease.equals(new net.javabeat.articles.spring3.el.model.Release('1.0'))]"; expression = parser.parseExpression(expressionStr); List allDefectsForRelease1_0 = expression.getValue(context, List.class); System.out.println("Defects for 1.0 Release...."); for (Defect defect : allDefectsForRelease1_0){ System.out.println(defect); } expressionStr = "Defects.?[forWhichRelease.equals(new net.javabeat.articles.spring3.el.model.Release('2.0')) " + " and assignee.equals(new net.javabeat.articles.spring3.el.model.Assignee('Assignee1'))]"; expression = parser.parseExpression(expressionStr); List allDefectsForRelease2_0AndAssignee1 = expression.getValue(context, List.class); System.out.println("Defects assigned for Assignee1 for 2.0 Release...."); for (Defect defect : allDefectsForRelease2_0AndAssignee1){ System.out.println(defect); } } @SuppressWarnings("unchecked") private static void testProjection(){ ExpressionParser parser = new SpelExpressionParser(); DTSystem system = getDTSystem(); EvaluationContext context = new StandardEvaluationContext(system); Expression expression = null; String expressionStr = "Defects.![forWhichRelease]"; expression = parser.parseExpression(expressionStr); List allReleases = new ArrayList(new HashSet(expression.getValue(context, List.class))); System.out.println("All Releases...."); for (Release release : allReleases){ System.out.println(release); } System.out.println(); expressionStr = "Defects.![assignee]"; expression = parser.parseExpression(expressionStr); List allAssignees = new ArrayList(new HashSet(expression.getValue(context, List.class))); System.out.println("All Assignees...."); for (Assignee assignee : allAssignees){ System.out.println(assignee); } } private static DTSystem getDTSystem(){ DTSystem system = new DTSystem(); Release release1 = new Release("1.0"); Release release2 = new Release("2.0"); Assignee assignee1 = new Assignee("Assignee1"); Assignee assignee2 = new Assignee("Assignee2"); Defect defect1 = new Defect("1", "Defect'1'", assignee1, release2); system.addDefect(defect1); Defect defect2 = new Defect("2", "Defect'2'", assignee2, release1); system.addDefect(defect2); Defect defect3 = new Defect("3", "Defect'3'", assignee1, release2); system.addDefect(defect3); Defect defect4 = new Defect("4", "Defect'4'", assignee2, release1); system.addDefect(defect4); Defect defect5 = new Defect("5", "Defect'5'", assignee1, release2); system.addDefect(defect5); return system; } }
Note the usage of Selections which is available as part of the method ‘testSelection()’. For this we have created a Defect tracking system and made it visible to the Evaluation Context object. Now this object has a combination of multiple release objects. Let us say that we want to filter all the defect objects that are targeted only for ‘1.0’ Release. For that we have used the syntax ‘CollectionObject.?[Fiter]’. In our example, we have used the filter ‘Defects.?[forWhichRelease.equals(new net.javabeat.articles.spring3.el.model.Release(‘1.0′))]’. In this expression ‘Defects’ is the collection which holds the list of defects available in the system. The expression ‘.?’ indicates that the expression following this will be applied for each object in the collection. This further means that for each defect the property ‘forWhichRelease’ will be checked for equality on the Release object ‘1.0’.
It is also possible to define projections on collection object. Let us say that we want to filter out all the release objects from the defect tracking system. For example, the expression ‘Defects.![forWhichRelease]’, has to be interpreted to return all the release objects from the collection. Note that the syntax used for projection is ‘.!’ as opposed to the syntax ‘.?’ which is used selection.
Modifying values in Spring Expressions
It is also possible to modify values that were previous set in the expression and the below example illustrates that. This example creates customer objects and sets it to the evaluation context. Next it creates a expression object for the expression string ‘name’.
package net.javabeat.articles.spring3.el; import net.javabeat.articles.spring3.el.model.Customer; import org.springframework.expression.EvaluationContext; import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; public class AssignmentTest { public static void main(String[] args) { ExpressionParser parser = new SpelExpressionParser(); Customer customer = new Customer("STV", "Steve"); EvaluationContext context = new StandardEvaluationContext(customer); Expression expression = null; expression = parser.parseExpression("name"); String customerName = expression.getValue(context, String.class); System.out.println("Customer name is " + customerName); expression.setValue(context, "Stephen"); customerName = expression.getValue(context, String.class); System.out.println("Customer's modified name is " + customerName); } }
Note that the initial value for the expression string ‘name’ will have the value ‘Steve’. The expression ‘name’ is now visible to context and the value can be changed by calling the expression object with the new value. This will change the value of the context variable ‘name’ and now the expression will yield the value ‘Stephen’.
Miscellaneous
In the final section of this article, we will see the miscellaneous on using Spring Expression. The first test method illustrates the usage of ‘T’ operator which is useful for obtaining class object for a given class. For example, ‘T(net.javabeat.articles.spring3.el.model.Customer)’ will return a class object for the Customer class. Similarly in the second test method is expected to return the class object for the class ‘AddressType’. Calling ‘valueOf()’ method will return the AddressType object for the given value string.
package net.javabeat.articles.spring3.el; import net.javabeat.articles.spring3.el.model.AddressType; import net.javabeat.articles.spring3.el.model.Customer; import org.springframework.expression.EvaluationContext; import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; public class MiscTest { public static void main(String[] args) { testTOperator(); test2(); testConstructor(); testSafeNull(); } private static void testTOperator(){ ExpressionParser parser = new SpelExpressionParser(); Expression expression = parser.parseExpression("T(net.javabeat.articles.spring3.el.model.Customer)"); Class<?> customerClassObject = expression.getValue(Class.class); System.out.println("Customer's class object is " + customerClassObject); } private static void test2(){ ExpressionParser parser = new SpelExpressionParser(); Expression expression = parser.parseExpression( "T(net.javabeat.articles.spring3.el.model.AddressType).valueOf('HOME')"); AddressType homeAddressType = expression.getValue(AddressType.class); System.out.println("Home address type is " + homeAddressType); } private static void testConstructor(){ ExpressionParser parser = new SpelExpressionParser(); Expression expression = parser.parseExpression( "new net.javabeat.articles.spring3.el.model.Customer('NEW', 'New Customer')"); Customer customer = expression.getValue(Customer.class); String customerId = customer.getId(); System.out.println("Customer Id is " + customerId); String customerName = customer.getName(); System.out.println("Customer name is " + customerName); } private static void testSafeNull(){ ExpressionParser parser = new SpelExpressionParser(); Customer customer = new Customer("TEST", "TEST"); customer.setAddresses(null); EvaluationContext context = new StandardEvaluationContext(customer); Expression expression = parser.parseExpression("addresses?.addressType"); AddressType addressType = expression.getValue(context, AddressType.class); System.out.println("Address type is " + addressType); } }
Type safety operator as designated through the test method ‘testSafeNull()’ can be used to avoid NullPointerExpection when expressions contain complex or hierarchy of objects. For example, the expression ‘addresses.addresType’ will result in a NullPointerException if the ‘addresses’ object is null. To avoid this kind of situation, the null-safe operator ‘?.’ can be used while accessing objects as the above example illustrates that.
Conclusion
This article gave an introduction in using various features that is available as part of Spring Expressions framework. Plenty of code samples were given for getting hands-on over various APIs. Writing simple expressions, complicated expressions, referencing bean objects, dealing with collections, writing custom functions and using them as expressions etc were discussed in detail. Hope the reader of this article will now know the usage of Spring Expression framework as well as can write complex expressions.
also read: