Spring3.0 —— 表达式语言(SpEL)

Spring3.0中新增加了Spring 表达式语言,可以在Spring 的任意需要配置的地方使用该功能。

Spring的表达式语言与Java注解结合,以便开发人员可以撰写和指向他们的配置,而不需要单独的XML文件写入,使得Spring开发者在不需要XML的情况下对应用进行配置。

SpelExpressionParser


@Test

publicvoid testClassTypeExpression() {

ExpressionParser parser = new SpelExpressionParser();

// java.lang包类访问

Class result1 = parser.parseExpression("T(String)").getValue(Class.class);

Assert.assertEquals(String.class, result1);

//其他包类访问

String expression2 = "T(cn.javass.spring.chapter5.SpELTest)";

Class 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);

}

基本表达式

    • 字面量表达式
    • 算术运算表达式
    • 比较运算表达式
    • 逻辑运算表达式
    • 字符串连接与截取表达式
    • 三目运算及Elivis运算表达式
    • 正则表达式


字面量表达式

字符串、数字类型(int、long、float、double)、布尔类型、null。

字符串

'Hello World!'"Hello World!"

数值类型

1-1L1.11.1E+20xa0xaL

布尔类型

truefalse

null

null


算术运算表达式

加(+)、减(-)、乘(*)、除(/或DIV)、求余(%MOD)、幂(^)。

加减乘除

1+2-3*4/21+2-3*4DIV2

求余

4%34MOD 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? :falsetrue?:false


正则表达式

正则表达式

matches

'123' matches '\\d{3}'

类相关表达式

    • 类类型表达式
    • 类实例化表达式
    • instanceof表达式
    • 变量定义及引用
    • 自定义函数
    • 赋值表达式
    • 对象属性存取及安全导航表达式
      • 访问root对象属性
      • 安全访问
      • root对象属性赋值
    • 对象方法调用
    • Bean引用


类类型表达式

使用"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
    • 内联Array
    • CollectionMap元素访问
    • CollectionMapArray元素修改
    • 集合投影
    • 集合选择


内联List

Spring3.0.4开始支持内联List,使用{表达式,……}定义内联List。如“{1,2,3}”将返回一个整型的ArrayList,而“{}”将返回空的List,对于字面量表达式列表,SpEL会使用java.util.Collections.unmodifiableList方法将列表设置为不可修改。

//将返回不可修改的空List

List result2 = parser.parseExpression("{}").getValue(List.class);

//对于列表中只要有一个不是字面量表达式,将只返回原始List,不会进行不可修改处理

String expression3 ="{{1+2,2+4},{3,4+4}}";

List> 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 collection =new HashSet();

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);

// SpELMap字典元素访问的支持

Map map =new HashMap();

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 collection =new ArrayList();

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 map =new HashMap();

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 collection =new ArrayList();

collection.add(4);

collection.add(5);

//===============测试集合或数组===============

EvaluationContext context1 =new StandardEvaluationContext();

context1.setVariable("collection", collection);

Collection 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 map =new HashMap();

map.put("a", 1);

map.put("b", 2);

// ===============测试Map ===============

EvaluationContext context2 =new StandardEvaluationContext();

context2.setVariable("map", map);

List 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 collection =new ArrayList();

collection.add(4);

collection.add(5);

// ===============集合或数组测试 ==============

EvaluationContext context1 =new StandardEvaluationContext();

context1.setVariable("collection", collection);

Collection result1 = parser.parseExpression("#collection.?[#this>4]").getValue(context1, Collection.class);

Assert.assertEquals(1, result1.size());

Assert.assertEquals(new Integer(5), result1.iterator().next());

Map map =new HashMap();

map.put("a", 1);

map.put("b", 2);

// =============== Map测试 ==============

EvaluationContext context2 =new StandardEvaluationContext();

context2.setVariable("map", map);

Map result2 = parser.parseExpression("#map.?[#this.key != 'a']").getValue(context2, Map.class);

Assert.assertEquals(1, result2.size());

List 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表示自定义变量,需要在上下文定义。

你可能感兴趣的:(java)