Spring3.0中新增加了Spring 表达式语言,可以在Spring 的任意需要配置的地方使用该功能。
Spring的表达式语言与Java注解结合,以便开发人员可以撰写和指向他们的配置,而不需要单独的XML文件写入,使得Spring开发者在不需要XML的情况下对应用进行配置。
SpelExpressionParser
@Test publicvoid testClassTypeExpression() { ExpressionParser parser = new SpelExpressionParser(); // java.lang包类访问 Class<String> result1 = parser.parseExpression("T(String)").getValue(Class.class); Assert.assertEquals(String.class, result1); //其他包类访问 String expression2 = "T(cn.javass.spring.chapter5.SpELTest)"; Class<String> result2 = parser.parseExpression(expression2).getValue(Class.class); Assert.assertEquals(SpELTest.class, result2); //类静态字段访问 int result3 = parser.parseExpression("T(Integer).MAX_VALUE").getValue(int.class); Assert.assertEquals(Integer.MAX_VALUE, result3); //类静态方法调用 int result4 = parser.parseExpression("T(Integer).parseInt('1')").getValue(int.class); } |
基本表达式
字面量表达式
字符串、数字类型(int、long、float、double)、布尔类型、null。
字符串 |
'Hello World!'、"Hello World!" |
数值类型 |
1、-1L、1.1、1.1E+2、0xa、0xaL |
布尔类型 |
true、false |
null |
null |
算术运算表达式
加(+)、减(-)、乘(*)、除(/或DIV)、求余(%或MOD)、幂(^)。
加减乘除 |
1+2-3*4/2、1+2-3*4DIV2 |
求余 |
4%3、4MOD 3 |
幂运算 |
2^3 |
比较运算表达式
不等于 |
!=或 ne |
1!= 1或 1ne 1 |
等于 |
==或 eq |
1== 1或 1eq 1 |
大于等于 |
>=或 ge |
1>= 1或 1ge 1 |
小于等于 |
<=或 le |
1<= 1或 1le 1 |
大于 |
>或 gt |
1> 1或 1gt 1 |
小于 |
<或 lt |
1< 1或 1lt 1 |
区间 |
between |
1 between {1, 2} |
|
逻辑运算表达式
与 |
AND |
true AND true |
或 |
OR |
true OR true |
非 |
NOT |
NOT true |
字符串连接及截取表达式
连接 |
+ |
'Hello' + 'World !' |
截取一个字符 |
string[index] |
'Hello World'[0] |
三目运算及Elivis运算表达式
三目运算符 |
表达式1?表达式2:表达式3 |
2>1?true:false |
Elivis运算 |
表达式1?:表达式2 |
null? :false或true?:false |
正则表达式
正则表达式 |
matches |
'123' matches '\\d{3}' |
类相关表达式
|
类类型表达式
使用"T(Type)"来表示java.lang.Class实例,"Type"必须是类全限定名(java.lang包除外)。使用类类型表达式还可以进行访问类静态方法及类静态字段。
实例 |
示例 |
java.lang包类访问 |
T(String) |
其他包的类访问 |
T(foo.bar.spel.SpELTest) |
类静态字段访问 |
T(Integer).MAX_VALUE |
类静态方法调用 |
T(Integer).parseInt('1') |
类实例化表达式
实例 |
示例 |
java.lang包类的实例化 |
new String('hello') |
其他包的类实例化 |
new java.util.Date() |
instanceof
实例 |
示例 |
Java内使用同义 |
'hello' instanceof T(String) |
变量定义与引用
变量定义通过EvaluationContext接口的setVariable(variableName, value)方法定义;在表达式中使用“#variableName”引用;除了引用自定义变量,SpEL还允许引用根对象及当前上下文对象,使用 “#root”引用根对象,使用“#this”引用当前上下文对象。“#this”引用当前上下文对象,此处“#this”即根对象。
@Test publicvoid testVariableExpression() { ExpressionParser parser =new SpelExpressionParser(); EvaluationContext context =new StandardEvaluationContext(); context.setVariable("variable","hello"); context.setVariable("variable","world");
String result1 = parser.parseExpression("#variable").getValue(context, String.class); Assert.assertEquals("world", result1);
context =new StandardEvaluationContext("hello"); String result2 = parser.parseExpression("#root").getValue(context, String.class); Assert.assertEquals("hello", result2);
String result3 = parser.parseExpression("#this").getValue(context, String.class); Assert.assertEquals("hello", result3); } |
自定义函数
目前只支持类静态方法注册为自定义函数;SpEL使用StandardEvaluationContext的registerFunction方法进行注册自定义函数,其实完全可以使用setVariable代替,两者其实本质是一样的。
@Test publicvoid testFunctionExpression()throws SecurityException, NoSuchMethodException { ExpressionParser parser = new SpelExpressionParser(); StandardEvaluationContext context = new StandardEvaluationContext(); Method parseInt = Integer.class.getDeclaredMethod("parseInt", String.class); context.registerFunction("parseInt", parseInt); context.setVariable("parseInt2", parseInt); String expression1 = "#parseInt('3') == #parseInt2('3')"; boolean result1 = parser.parseExpression(expression1).getValue(context,boolean.class); Assert.assertEquals(true, result1); } |
赋值表达式
SpEL即允许给自定义变量赋值,也允许给跟对象赋值,直接使用“#variableName=value”即可赋值:
@Test publicvoid testAssignExpression() { ExpressionParser parser = new SpelExpressionParser(); // 1.给root对象赋值 EvaluationContext context = new StandardEvaluationContext("aaaa"); String result1 = parser.parseExpression("#root='aaaaa'").getValue(context, String.class); Assert.assertEquals("aaaaa", result1); String result2 = parser.parseExpression("#this='aaaa'").getValue(context, String.class); Assert.assertEquals("aaaa", result2); // 2.给自定义变量赋值 context.setVariable("#variable","variable"); String result3 = parser.parseExpression("#variable=#root").getValue(context, String.class); Assert.assertEquals("aaaa", result3); } |
对象属性存取及安全导航表达式
对象属性获取非常简单,即使用如“a.property.property”这种点缀式获取,SpEL对于属性名首字母是不区分大小写的。
给对象属性赋值可以采用赋值表达式或Expression接口的setValue方法赋值,而且也可以采用点缀方式赋值。
SpEL还引入了Groovy语言中的安全导航运算符“(对象|属性)?.属性”,用来避免当“?.”前边的表达式为null时抛出空指针异常,而是返回null。
ExpressionParser parser =new SpelExpressionParser(); // ===============访问root对象属性 =============== Date date =new Date(); StandardEvaluationContext context =new StandardEvaluationContext(date); int result1 = parser.parseExpression("Year").getValue(context,int.class); Assert.assertEquals(date.getYear(), result1); int result2 = parser.parseExpression("year").getValue(context,int.class); Assert.assertEquals(date.getYear(), result2); // ===============安全访问 =============== context.setRootObject(null); Object result3 = parser.parseExpression("#root?.year").getValue(context, Object.class); Assert.assertEquals(null, result3); // ===============给root对象属性赋值 =============== context.setRootObject(date); int result4 = parser.parseExpression("Year = 4").getValue(context,int.class); Assert.assertEquals(4, result4); parser.parseExpression("Year").setValue(context, 5); int result5 = parser.parseExpression("Year").getValue(context,int.class); Assert.assertEquals(5, result5); |
对象方法调用
对象方法调用更简单,跟Java语法一样;如“'helo'.substring(2,4)”将返回“lo”。对于根对象可以直接调用方法。
ExpressionParser parser =new SpelExpressionParser(); // ===============直接调用对象方法 =============== String result1 = parser.parseExpression("'hello'.substring(3)").getValue(String.class); Assert.assertEquals("lo", result1); // ===============调用上下文root对象方法 =============== Date date =new Date(); StandardEvaluationContext context =new StandardEvaluationContext(date); int result2 = parser.parseExpression("getYear()").getValue(context,int.class); Assert.assertEquals(date.getYear(), result2); |
Bean引用
SpEL支持使用“@”符号来引用Bean,在引用Bean时需要使用BeanResolver接口实现来查找Bean,Spring提供BeanFactoryResolver实现。
在示例中首先初始化了一个IoC容器,ClassPathXmlApplicationContext 实现默认会把“System.getProperties()”注册为“systemProperties”Bean,因此使用 “@systemProperties”来引用该Bean。
@Test publicvoid testBeanExpression() { ClassPathXmlApplicationContext ctx =new ClassPathXmlApplicationContext(); ctx.refresh(); ExpressionParser parser =new SpelExpressionParser(); StandardEvaluationContext context =new StandardEvaluationContext(); context.setBeanResolver(new BeanFactoryResolver(ctx)); Properties result1 = parser.parseExpression("@systemProperties").getValue(context, Properties.class); Assert.assertEquals(System.getProperties(), result1); } |
集合相关表达式
|
内联List
从Spring3.0.4开始支持内联List,使用{表达式,……}定义内联List。如“{1,2,3}”将返回一个整型的ArrayList,而“{}”将返回空的List,对于字面量表达式列表,SpEL会使用java.util.Collections.unmodifiableList方法将列表设置为不可修改。
//将返回不可修改的空List List<Integer> result2 = parser.parseExpression("{}").getValue(List.class); |
//对于列表中只要有一个不是字面量表达式,将只返回原始List,不会进行不可修改处理 String expression3 ="{{1+2,2+4},{3,4+4}}"; List<List<Integer>> result3 = parser.parseExpression(expression3).getValue(List.class); result3.get(0).set(0, 1); Assert.assertEquals(2, result3.size()); |
内联Array
//声明一维数组并初始化值 int[] result2 = parser.parseExpression("new int[]{1,2}").getValue(int[].class); Assert.assertEquals(result2[0], 1); |
//定义多维数组但不初始化(定义多维数组不能初始化) int[][][] result3 = parser.parseExpression("new int[2][2][2]").getValue(int[][][].class); |
集合,字典元素访问
SpEL目前支持所有集合类型和字典类型的元素访问,使用“集合[索引]”访问集合元素,使用“map[key]”访问字典元素。
集合元素访问是通过Iterator遍历来定位元素位置的。
// SpEL内联List访问 int result1 = parser.parseExpression("{1,2,3}[0]").getValue(int.class); //即list.get(0) Assert.assertEquals(1, result1); |
// SpEL目前支持所有集合类型的访问 Collection<Integer> collection =new HashSet<Integer>(); collection.add(1); collection.add(2); EvaluationContext context2 =new StandardEvaluationContext(); context2.setVariable("collection", collection); int result2 = parser.parseExpression("#collection[1]").getValue(context2,int.class); //对于任何集合类型通过Iterator来定位元素 Assert.assertEquals(2, result2); |
// SpEL对Map字典元素访问的支持 Map<String, Integer> map =new HashMap<String, Integer>(); map.put("a", 1); EvaluationContext context3 =new StandardEvaluationContext(); context3.setVariable("map", map); int result3 = parser.parseExpression("#map['a']").getValue(context3,int.class); Assert.assertEquals(1, result3); |
列表,字典,数组元素修改
可以使用赋值表达式或Expression接口的setValue方法修改。对数组修改直接对“#array[index]”赋值即可修改元素值,同理适用于集合和字典类型。
// ===============修改数组元素值 =============== int[] array =newint[] { 1, 2 }; EvaluationContext context1 =new StandardEvaluationContext(); context1.setVariable("array", array); int result1 = parser.parseExpression("#array[1] = 3").getValue(context1,int.class); Assert.assertEquals(3, result1); |
// ===============修改集合值 =============== Collection<Integer> collection =new ArrayList<Integer>(); collection.add(1); collection.add(2); EvaluationContext context2 =new StandardEvaluationContext(); context2.setVariable("collection", collection); int result2 = parser.parseExpression("#collection[1] = 3").getValue(context2,int.class); Assert.assertEquals(3, result2); parser.parseExpression("#collection[1]").setValue(context2, 4); result2 = parser.parseExpression("#collection[1]").getValue(context2,int.class); Assert.assertEquals(4, result2); |
// ===============修改map元素值 =============== Map<String, Integer> map =new HashMap<String, Integer>(); map.put("a", 1); EvaluationContext context3 =new StandardEvaluationContext(); context3.setVariable("map", map); int result3 = parser.parseExpression("#map['a'] = 2").getValue(context3,int.class); Assert.assertEquals(2, result3); |
集合投影
在SQL中投影指从表中选择出列,而在SpEL指根据集合中的元素中通过选择来构造另一个集合,该集合和原集合具有相同数量的元素;SpEL使用“(list|map).![投影表达式]”来进行投影运算:
Collection<Integer> collection =new ArrayList<Integer>(); collection.add(4); collection.add(5); //===============测试集合或数组=============== EvaluationContext context1 =new StandardEvaluationContext(); context1.setVariable("collection", collection); Collection<Integer> result1 = parser.parseExpression("#collection.![#this+1]").getValue(context1, Collection.class); Assert.assertEquals(2, result1.size()); Assert.assertEquals(new Integer(5), result1.iterator().next()); |
对于集合或数组使用如上表达式进行投影运算,其中投影表达式中“#this”代表每个集合或数组元素,可以使用比如“#this.property”来获取集合元素的属性,其中“#this”可以省略。
Map<String, Integer> map =new HashMap<String, Integer>(); map.put("a", 1); map.put("b", 2); // ===============测试Map =============== EvaluationContext context2 =new StandardEvaluationContext(); context2.setVariable("map", map); List<Integer> result2 = parser.parseExpression("#map.![value+1]").getValue(context2, List.class); Assert.assertEquals(2, result2.size()); |
SpEL投影运算还支持Map投影,但Map投影最终只能得到List结果,如上所示,对于投影表达式中的“#this”将是Map.Entry,所以可以使用“value”来获取值,使用“key”来获取键。
集合选择
在SQL中指使用select进行选择行数据,而在SpEL指根据原集合通过条件表达式选择出满足条件的元素并构造为新的集合,SpEL使用“(list|map).?[选择表达式]”,其中选择表达式结果必须是boolean类型,如果true则选择的元素将添加到新集合中,false将不添加到新集合中。
Collection<Integer> collection =new ArrayList<Integer>(); collection.add(4); collection.add(5); // ===============集合或数组测试 ============== EvaluationContext context1 =new StandardEvaluationContext(); context1.setVariable("collection", collection); Collection<Integer> result1 = parser.parseExpression("#collection.?[#this>4]").getValue(context1, Collection.class); Assert.assertEquals(1, result1.size()); Assert.assertEquals(new Integer(5), result1.iterator().next()); |
Map<String, Integer> map =new HashMap<String, Integer>(); map.put("a", 1); map.put("b", 2); // =============== Map测试 ============== EvaluationContext context2 =new StandardEvaluationContext(); context2.setVariable("map", map); Map<String, Integer> result2 = parser.parseExpression("#map.?[#this.key != 'a']").getValue(context2, Map.class); Assert.assertEquals(1, result2.size()); List<Integer> result3 = parser.parseExpression("#map.?[key != 'a'].![value+1]").getValue(context2, List.class); Assert.assertEquals(new Integer(3), result3.iterator().next()); |
对于Map选择,如“#map.?[#this.key != 'a']”将选择键值不等于”a”的,其中选择表达式中“#this”是Map.Entry类型,而最终结果还是Map,这点和投影不同;集合选择和投影可以一起使用,如“#map.?[key != 'a'].![value+1]”将首先选择键值不等于”a”的,然后在选出的Map中再进行“value+1”的投影。
表达式模板
模板表达式就是由字面量与一个或多个表达式块组成。每个表达式块由“前缀+表达式+后缀”形式组成,如“${1+2}”即表达式块。在前边我们已经介绍了使用ParserContext接口实现来定义表达式是否是模板及前缀和后缀定义。在此就不多介绍了,如“Error ${#v0} ${#v1}”表达式表示由字面量“Error ”、模板表达式“#v0”、模板表达式“#v1”组成,其中v0和v1表示自定义变量,需要在上下文定义。