7.12 Spring提供的表达式语言(SpEL) 7.12.1 使用Expression接口进行表达式求值

7.12 Spring提供的表达式语言(SpEL)

Spring表达式语言(简称SpEL)是一种与JSP 2EL功能类似的表达式语言,它可以在运行时查询和操作对象。与JSP 2EL相比,SpEL功能更加强大,它甚至支持方法调用和基本字符串模板函数

  • SpEL可以独立于Spring容器使用——只是当成简单的表达式语言来使用;
  • 也可以在注解或XMI配置中使用SpEL,这样可以充分利用SpEL简化SpringBean配置

7.12.1 使用Expression接口进行表达式求值

SpringSpEL可以单独使用,可以使用SpEL对表达式计算、求值

SpEL接口

SpEL主要提供了如下三个接口。

接口 描述
ExpressionParser 该接口的实例负责解析一个SpEL表达式,返回一个Expression对象。
Expression 该接口的实例代表一个表达式
EvaluationContext 代表计算表达式值的上下文。当SpEL表达式中含有变量时,程序将需要使用该API来计算表达式的值。

Expression接口介绍

Expression实例代表一个表达式,它包含了下面这些重载的getValue方法用于计算表达式的值。

方法 描述
Object getValue() 计算表达式的值。
<T> T getValue(Class<T> desiredResultType) 计算表达式的值,而且尝试将该表达式的值当成desiredResultType类型处理。
Object getValue(Evaluation ContextContext) 使用指定的EvaluationContext来计算表达式的值。
<T> T getValue(Evaluation ContextContext, Class<T> desiredResultType) 使用指定的EvaluationContext来计算表达式的值,而且尝试将该表达式的值当成desiredResultType类型处理.
Object getValue(Object rootObject) rootObject作为表达式的root对象来计算表达式的值。
<T> T getValue(Object rootObject,Class<T> desiredResultType) rootObject作为表达式的root对象来计算表达式的值,而且尝试将该表达式的值当成desiredResultType类型处理。

程序示例

1
2
3
4
5
6
7
8
E:\workspace_QingLiangJiJavaEEQiYeYingYongShiZhang5\Expression\src
├─lee\
│ └─SpELTest.java
└─org\
└─crazyit\
└─app\
└─domain\
└─Person.java

SpELTest.java

下面的程序示范了如何利用ExpressionParserExpression来计算表达式的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package lee;

import org.springframework.expression.*;
import org.springframework.expression.spel.standard.*;
import org.springframework.expression.spel.support.*;

import java.util.*;

import org.crazyit.app.domain.*;

public class SpELTest
{
public static void main(String[] args)
{
// 创建一个ExpressionParser对象,用于解析表达式
ExpressionParser parser = new SpelExpressionParser();
// 最简单的字符串表达式
Expression exp = parser.parseExpression("'HelloWorld'");
System.out.println("'HelloWorld'的结果: " + exp.getValue());
// 调用方法的表达式
exp = parser.parseExpression("'HelloWorld'.concat('!')");
System.out.println("'HelloWorld'.concat('!')的结果: "
+ exp.getValue());
// 调用对象的getter方法
exp = parser.parseExpression("'HelloWorld'.bytes");
System.out.println("'HelloWorld'.bytes的结果: "
+ exp.getValue());
// 访问对象的属性(d相当于HelloWorld.getBytes().length)
exp = parser.parseExpression("'HelloWorld'.bytes.length");
System.out.println("'HelloWorld'.bytes.length的结果:"
+ exp.getValue());
// 使用构造器来创建对象
exp = parser.parseExpression("new String('helloworld')"
+ ".toUpperCase()");
System.out.println("new String('helloworld')"
+ ".toUpperCase()的结果是: "
+ exp.getValue(String.class));
Person person = new Person(1 , "孙悟空", new Date());
exp = parser.parseExpression("name");
// 以指定对象作为root来计算表达式的值
// 相当于调用person.name表达式的值
System.out.println("以persn为root,name表达式的值是: "
+ exp.getValue(person , String.class));
exp = parser.parseExpression("name=='孙悟空'");
StandardEvaluationContext ctx = new StandardEvaluationContext();
// 将person设为Context的root对象
ctx.setRootObject(person);
// 以指定Context来计算表达式的值
System.out.println(exp.getValue(ctx , Boolean.class));
List<Boolean> list = new ArrayList<Boolean>();
list.add(true);
EvaluationContext ctx2 = new StandardEvaluationContext();
// 将list设置成EvaluationContext的一个变量
ctx2.setVariable("list" , list);
// 修改list变量的第一个元素的值
parser.parseExpression("#list[0]").setValue(ctx2 , "false");
// list集合的第一个元素被改变
System.out.println("list集合的第一个元素为:"
+ parser.parseExpression("#list[0]").getValue(ctx2));
}
}

上面程序中的代码使用ExpressionParser多次解析了不同类型的表达式,

  • ExpressionParser调用parseExpression方法将返回一个Expression实例(表达式对象)。
  • 调用Expression对象的getValue方法即可获取该表达式的值。

EvaluationContext代表SpEL计算表达式值的”上下文”,这个Context对象可以包含多个对象,但只能有一个root(根)对象。
EvaluationContext的作用有点类似于前面介绍的OGNL中的StackContext,EvaluationContext可以包含多个对象,但只能有一个root对象。当表达式中包含变量时,SpEL就会根据EvaluationContext中变量的值对表达式进行计算。

往EvaluationContext里面放入变量

为了往EvaluationContext里放入对象(SpEL称之为变量),可以调用该对象的如下方法:
setVariable(String name,Object value):向EvaluationContext中放入value对象,该对象名为name为了在SpEL访问Evaluation context中指定对象,应采用与OGNL类似的格式:

1
#name

StandardEvaluationContext设置root对象

StandardEvaluationContext提供了setRootObject(Object rootObject)法来设置root对象。

SpEL中访问root对象的属性时,可以省略root对象前缀,例如如下代码:

1
foo.bar //访问 rootObject的foo属性的bar属性

当然,使用Expression对象计算表达式的值时,也可以直接指定root对象,例如上面程序中的粗体字代码:

1
exp.getValue(person, String.class) //以`person`对象为`root`对象计算表达式的值

Person.java

上面的程序中使用了一个简单的Person类,它只是一个普通的Java Bean:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package org.crazyit.app.domain;
import java.util.Date;
public class Person
{
private Integer id;
private String name;
private Date birth;
// 无参数的构造器
public Person()
{
}
// 初始化全部成员变量的构造器
public Person(Integer id , String name , Date birth)
{
this.id = id;
this.name = name;
this.birth = birth;
}
// 此处省略getter和setter方法
}