Spring 3.0 - Expression Language Support

>> Saturday, October 10, 2009

One of the new features in Spring 3.0 is the Spring Expression Language (Spring EL or SpEL). While evaluation Spring 3.0 I was also checking out the SpEL capabilities, in this blog entry I'll try to cover some of the more interesting, and less obvious, features and aspects of the SpEL.

The Basics


In its basics SpEL is yet another Expression Language, similar to Unified EL, it supports expressions (no control statements in the language) mainly used to access bean properties, the Spring Expression Language can be used as part of the Spring bean factory configuration (XML or annotation based) but can also be evaluated directly by the application code - meaning we can read and evaluate expressions at runtime. Here is the most basic example of SpEL used in
XML factory bean:

<!-- Classic - Simple bean with random value -->
<bean id="randomNumber" class="java.lang.Math" factory-method="random"/>
<bean id="classicBean" class="com.jroller.blogs.eyallupu.MyBean" p:random-ref="randomNumber"/>


<!-- SpEL - Getting the value of the Random bean -->
<bean id="elBean" class="com.jroller.blogs.eyallupu.MyBean" p:random="#{randomNumber}"/>





In the sample above we have the 'classic' section which is using the old standard XML configuration to create two beans, the 'randomNumber' bean and the 'classicBean' bean. The reference to the randomNumber is done using the "p:randon-ref" attribute. In the SpEL section of the example the 'elBean' uses the '#{randomNumber}' expression to reference to 'randomNumber' bean so both 'classicBean' and 'elBean' share the same random value. Beside a first look of the SpEL syntax there is no much use for the expressoin languagae in the example above, we only replaced the 'randomNumber' bean reference with the '#{randomNumber}' expression and we don't gain much out of it. But consider the following example, let's assume that MyBean code is as follows:

public class MyBean implements BeanNameAware{

  private double random = Math.random(); //(1)

  private String beanName;

  public double getRandom() {
    return random;
  }

  public void setRandom(double random) { //(2)
    this.random = random;
  }
  ...
}

The 'random' property value can be set either using the instance variable initializer (1) or using its setter (2). Looking again at the previous example it is obvious that it can easily break - if, in the future, someone decide not to use the classicBean's random property setter but to stick with the auto initialized value of the field then value of elBean's random property will not be identical to the one of the classicBean. SpEL is a great way to easily access the actual value of the 'random' property of the 'classicBean':

<!-- Classic - Simple bean with random value which is not being set from the setter-->
<bean id="classicBean" class="com.jroller.blogs.eyallupu.MyBean"/>

<!-- EL - Getting the value of the Random bean -->
<bean id="elBean" class="com.jroller.blogs.eyallupu.MyBean" p:random="#{classicBean.random}"/>



SpEL Extensions


The two examples above illustrated the very basics of SpEL, in first look it seems to be very similar to Unified EL and in practice it is but is has some interesting features, in this section I want to describe some of these features.

Types


SpEL can access instances of java.lang.Class using the T operator (T stands for Type), for example:

T(java.util.Math)
T(Integer)            //For classes in the java.lang package there is no need to specify the package

Below are some samples using the T operator:
Static Method Invocation and Static Members access

If we can access the java.lang.Class object we would like to access static members - methods and constants - too. The two examples in the box bellow illustrate static methods invocations. I invoke the Math.random() method and the String.format() method (remember that the signature of the String.format() method includes a string argument and a varargs - from the example we see that SpEL supports method invocation using simple and varargs arguments):

<bean id="elBean" class="com.jroller.blogs.eyallupu.MyBean" p:random="#{T(java.util.Math).random()}"/>

<bean id="elBean2" class="...." p:str="#{T(String).format('Hello %s', 'world')}" //Notice that strings are enclosed with a single quote


The following example illustrates accessing a static field, it gets the value of the Double.MAX_VALUE static field:

<bean id="elBean" class="com.jroller.blogs.eyallupu.MyBean" p:random="#{T(Double).MAX_VALUE}"/>

Instanceof

If we have references to types we can also try and validate if a SpEL expression is of a specific type - this is done using the instanceof operator:

<bean id="elBean" class="..." p:objectProp="A string" />

<!-- EL - instanceof -->
<bean id="elBean2" class="..." p:strProp="#{elBean.objectProp instanceof T(String) ? 'yes' : 'no'}" />

The sample above illustrates access to another bean's property (elBean.objectProp) and invoking the 'instanceof' operator on its value. If the value is a String the expression returns 'yes' otherwise 'no'.

Constructors

Another thing we can do with types (but there is no need for the T operator) is to create new instances, this can be done using the new operator:

<bean id="elBean3" class="..." p:strProp="#{new java.io.File('/tmp/a.a').exists()}" />

The sample above illustrates the new operator but is also illustrates the introduction of some logic into the process of setting value to the 'strProp'. I set the strProp value to 'true' if a specific file exists or to false otherwise, this is done by using the java.io.File(String) constructor with '/tmp/a.a' as a file path and by invoking the exists() method on the newly created File object. Notice that SpEL automatically does the conversion from boolean to string.


Collections - Selections and Projections

SpEL supports collections selections and projections. Using selection we can create a new collection containing a subset of a source collection, the syntax of a selection operator is ?[selection-expression] and to invoke it on a specific collection the following syntax is used: collection-name.?[selection-expression]  (notice that a collection can also be a map - more in that below), let's assume that we have a bean which is a list of car manufacturers:

<util:list id="automakers">
  <value>Acura</value>
  <value>Audi</value>
  <value>BMW</value>
  <value>Buick</value>
  <value>Lexus</value>
  <value>Mazda</value>
  <value>Mercedes-Benz USA</value>
  <value>Mitsubishi</value>
  <value>Suzuki</value>
  <value>Toyota</value>
  <value>Volvo</value>
</util:list>

If we would like to obtain a new collection which includes only manufacturers which their name starts with 'M' the following selection will do the work: #{automakers.?[startsWith('M')]} for example:

<util:map id="beanName" value-type="java.lang.Object" key-type="java.lang.String">
   <entry key="starts-with-M" value="#{automakers.?[startsWith('M')]}"/>
</util:map>

This will create the collection: [Mazda, Mercedes-Benz USA, Mitsubishi], if we would like to find only the first name which starts with 'M' we could use the following expression:  #{automakers.^[startsWith('M')]} and to find the last one we would substitute the ^ with $. As mentioned before selections also work for Maps, we just have to remember that the member of a map is Map.Entry and to build our selection according to that:

<util:map id="numbersMap" value-type="java.lang.Integer" key-type="java.lang.String">
   <entry key="one" value="1"/>
   <entry key="two" value="2"/>
   <entry key="three" value="3"/>
   <entry key="four" value="4"/>
   <entry key="five" value="5"/>
</util:map>

If we would like to create a new subset map containing the entries 'one' and 'two' the follwoing selection expression will do the work: numbersMap.?[key=='one' or key=='two'] notice that since the source type was a map the selection output will be a map as well.

A projection creates new collection (or map) out of the results of an expression evaluated on the source collection elements (the source cannot be a set). The syntax for creating a projection is collection-name.![expression-on-elements] (Notice that I use ! and not ? as in the selection). For example the expression #{automakers.![length()]} creates a new collection containing the lengths of the auto maker names ([5, 4, 3, 5, 5, 5, 17, 10, 6, 6, 5]). Of course this could be more meaningful in a real life scenario.

Regular expressions


Last one on this blog entry is regular expressions, SpEL supports regular expressions using the matches operator, this is a relational operator returning true or false. Here is an example:

<util:map id="regExpsSamples" value-type="java.lang.Object" key-type="java.lang.String">
   <entry key="# {'a string' matches 'a.*'}" value="#{'a string' matches 'b.*'}"/>
</util:map>

We can use any regular expression as we would like, for example I could use the '[Mm].*' expression to create the selection illustrated above.

Summary


As said these are only few samples I tried during evaluation I've made to Spring 3.0 and I'm sure these can be used in many scenarios - don't forget that the expressions can be evaluated by code (using Spring's API) so we can dynamically use it.

3 comments:

Ramesh Mandaleeka July 29, 2010 at 5:05 AM  

Nice post. Examples didn't cover "scope" property how it helps to resolve the SpEL expressions. For example Dependency injection of HttpSession variables etc

Anonymous October 4, 2010 at 2:14 PM  

ЎUf, me gustу! Tan clara y positiva.

[url=http://www.lub4life.com/]Edwas[/url]

digital certificate March 1, 2015 at 4:50 AM  

Excellent! Thanks for this - I've been looking at this feature for ages. Followed your instructions and it works a treat!

  © Blogger templates Sunset by Ourblogtemplates.com 2008

Back to TOP