5.3  SpEL语法

5.3.1  基本表达式

一、字面量表达式: SpEL支持的字面量包括:字符串、数字类型(int、long、float、double)、布尔类型、null类型。

类型

示例

字符串

String str1 = parser.parseExpression("'Hello World!'").getValue(String.class);

String str2 = parser.parseExpression("\"Hello World!\"").getValue(String.class);

数字类型

int int1 = parser.parseExpression("1").getValue(Integer.class);

long long1 = parser.parseExpression("-1L").getValue(long.class);

float float1 = parser.parseExpression("1.1").getValue(Float.class);

double double1 = parser.parseExpression("1.1E+2").getValue(double.class);

int hex1 = parser.parseExpression("0xa").getValue(Integer.class);

long hex2 = parser.parseExpression("0xaL").getValue(long.class);

布尔类型

boolean true1 = parser.parseExpression("true").getValue(boolean.class);

boolean false1 = parser.parseExpression("false").getValue(boolean.class);

null类型

Object null1 = parser.parseExpression("null").getValue(Object.class);

 

二、算数运算表达式: SpEL支持加(+)、减(-)、乘(*)、除(/)、求余(%)、幂(^)运算。

类型

示例

加减乘除

int result1 = parser.parseExpression("1+2-3*4/2").getValue(Integer.class);//-3

求余

int result2 = parser.parseExpression("4%3").getValue(Integer.class);//1

幂运算

int result3 = parser.parseExpression("2^3").getValue(Integer.class);//8

SpEL还提供求余(MOD)和除(DIV)而外两个运算符,与“%”和“/”等价,不区分大小写。

三、关系表达式:等于(==)、不等于(!=)、大于(>)、大于等于(>=)、小于(<)、小于等于(<=),区间(between)运算,如“parser.parseExpression("1>2").getValue(boolean.class);”将返回false;而“parser.parseExpression("1 between {1, 2}").getValue(boolean.class);”将返回true。

between运算符右边操作数必须是列表类型,且只能包含2个元素。第一个元素为开始,第二个元素为结束,区间运算是包含边界值的,即 xxx>=list.get(0) && xxx<=list.get(1)。

SpEL同样提供了等价的“EQ” 、“NE”、 “GT”、“GE”、 “LT” 、“LE”来表示等于、不等于、大于、大于等于、小于、小于等于,不区分大小写。

四、逻辑表达式:且(and)、或(or)、非(!或NOT)。

String expression1 = "2>1 and (!true or !false)";
boolean result1 = parser.parseExpression(expression1).getValue(boolean.class);
Assert.assertEquals(true, result1); String expression2 = "2>1 and (NOT true or NOT false)";
boolean result2 = parser.parseExpression(expression2).getValue(boolean.class);
Assert.assertEquals(true, result2);

注:逻辑运算符不支持 Java中的 && 和 || 

五、字符串连接及截取表达式:使用“+”进行字符串连接,使用“'String'[0] [index]”来截取一个字符,目前只支持截取一个,如“'Hello ' + 'World!'”得到“Hello World!”;而“'Hello World!'[0]”将返回“H”。

六、三目运算及Elivis运算表达式:

三目运算符 “表达式1?表达式2:表达式3用于构造三目运算表达式,如“2>1?true:false”将返回true;

Elivis运算符“表达式1?:表达式2从Groovy语言引入用于简化三目运算符的,当表达式1为非null时则返回表达式1,当表达式1为null时则返回表达式2,简化了三目运算符方式“表达式1? 表达式1:表达式2”,如“null?:false”将返回false,而“true?:false”将返回true;

七、正则表达式:使用“str matches regex,如“'123' matches '\\d{3}'”将返回true;

八、括号优先级表达式:使用“(表达式)”构造,括号里的具有高优先级。

5.3.3  类相关表达式

一、类类型表达式:使用“T(Type)”来表示java.lang.Class实例,“Type”必须是类全限定名,“java.lang”包除外,即该包下的类可以不指定包名;使用类类型表达式还可以进行访问类静态方法及类静态字段。

具体使用方法如下:

@Test
public void 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);
Assert.assertEquals(1, result4);
}

对于java.lang包里的可以直接使用“T(String)”访问;其他包必须是类全限定名;可以进行静态字段访问如“T(Integer).MAX_VALUE”;也可以进行静态方法访问如“T(Integer).parseInt('1')”。

二、类实例化:类实例化同样使用java关键字“new”,类名必须是全限定名,但java.lang包内的类型除外,如String、Integer。

@Test
public void testConstructorExpression() {
ExpressionParser parser = new SpelExpressionParser();
String result1 = parser.parseExpression("new String('haha')").getValue(String.class);
Assert.assertEquals("haha", result1);
Date result2 = parser.parseExpression("new java.util.Date()").getValue(Date.class);
Assert.assertNotNull(result2);
}

实例化完全跟Java内方式一样。

三、instanceof表达式:SpEL支持instanceof运算符,跟Java内使用同义;如“'haha' instanceof T(String)”将返回true。

四、变量定义及引用:变量定义通过EvaluationContext接口的setVariable(variableName, value)方法定义;在表达式中使用“#variableName”引用;除了引用自定义变量,SpE还允许引用根对象及当前上下文对象,使用“#root”引用根对象,使用“#this”引用当前上下文对象;

@Test
public void testVariableExpression() {
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = new StandardEvaluationContext();
context.setVariable("variable", "haha");
context.setVariable("variable", "haha");
String result1 = parser.parseExpression("#variable").getValue(context, String.class);
Assert.assertEquals("haha", result1); context = new StandardEvaluationContext("haha");
String result2 = parser.parseExpression("#root").getValue(context, String.class);
Assert.assertEquals("haha", result2);
String result3 = parser.parseExpression("#this").getValue(context, String.class);
Assert.assertEquals("haha", result3);
}

使用“#variable”来引用在EvaluationContext定义的变量;除了可以引用自定义变量,还可以使用“#root”引用根对象,“#this”引用当前上下文对象,此处“#this”即根对象。

五、自定义函数:目前只支持类静态方法注册为自定义函数;SpEL使用StandardEvaluationContext的registerFunction方法进行注册自定义函数,其实完全可以使用setVariable代替,两者其实本质是一样的;

@Test
public void 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);
}

此处可以看出“registerFunction”和“setVariable”都可以注册自定义函数,但是两个方法的含义不一样,推荐使用“registerFunction”方法注册自定义函数。

六、赋值表达式:SpEL即允许给自定义变量赋值,也允许给跟对象赋值,直接使用“#variableName=value”即可赋值:

@Test
public void 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);
}

使用“#root='aaaaa'”给根对象赋值,使用“"#this='aaaa'”给当前上下文对象赋值,使用“#variable=#root”给自定义变量赋值,很简单。

七、对象属性存取及安全导航表达式:对象属性获取非常简单,即使用如“a.property.property”这种点缀式获取,SpEL对于属性名首字母是不区分大小写的;SpEL还引入了Groovy语言中的安全导航运算符“(对象|属性)?.属性”,用来避免但“?.”前边的表达式为null时抛出空指针异常,而是返回null;修改对象属性值则可以通过赋值表达式或Expression接口的setValue方法修改。

ExpressionParser parser = new SpelExpressionParser();
//1.访问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);

对于当前上下文对象属性及方法访问,可以直接使用属性或方法名访问,比如此处根对象date属性“year”,注意此处属性名首字母不区分大小写。

//2.安全访问
context.setRootObject(null);
Object result3 = parser.parseExpression("#root?.year").getValue(context, Object.class);
Assert.assertEquals(null, result3);

SpEL引入了Groovy的安全导航运算符,比如此处根对象为null,所以如果访问其属性时肯定抛出空指针异常,而采用“?.”安全访问导航运算符将不抛空指针异常,而是简单的返回null。

//3.给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);

给对象属性赋值可以采用赋值表达式或Expression接口的setValue方法赋值,而且也可以采用点缀方式赋值。

八、对象方法调用:对象方法调用更简单,跟Java语法一样;如“'haha'.substring(2,4)”将返回“ha”;而对于根对象可以直接调用方法;

Date date = new Date();
StandardEvaluationContext context = new StandardEvaluationContext(date);
int result2 = parser.parseExpression("getYear()").getValue(context, int.class);
Assert.assertEquals(date.getYear(), result2);

比如根对象date方法“getYear”可以直接调用。

九、Bean引用:SpEL支持使用“@”符号来引用Bean,在引用Bean时需要使用BeanResolver接口实现来查找Bean,Spring提供BeanFactoryResolver实现;

@Test
public void 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);
}

在示例中我们首先初始化了一个IoC容器,ClassPathXmlApplicationContext 实现默认会把“System.getProperties()”注册为“systemProperties”Bean,因此我们使用 “@systemProperties”来引用该Bean。

5.3.3  集合相关表达式

一、内联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
List<Integer> result1 = parser.parseExpression("{1,2,3}").getValue(List.class);
Assert.assertEquals(new Integer(1), result1.get(0));
try {
result1.set(0, 2);
//不可能执行到这,对于字面量列表不可修改
Assert.fail();
} catch (Exception e) {
}
//对于列表中只要有一个不是字面量表达式,将只返回原始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());
//声明一个大小为2的一维数组并初始化
int[] result2 = parser.parseExpression("new int[2]{1,2}").getValue(int[].class);
//定义一维数组但不初始化
int[] result1 = parser.parseExpression("new int[1]").getValue(int[].class);

二、内联数组:和Java 数组定义类似,只是在定义时进行多维数组初始化。

//定义多维数组但不初始化
int[][][] result3 = parser.parseExpression("new int[1][2][3]").getValue(int[][][].class);
//错误的定义多维数组,多维数组不能初始化
String expression4 = "new int[1][2][3]{{1}{2}{3}}";
try {
int[][][] result4 = parser.parseExpression(expression4).getValue(int[][][].class);
Assert.fail();
} catch (Exception e) {
}

三、集合,字典元素访问:SpEL目前支持所有集合类型和字典类型的元素访问,使用“集合[索引]”访问集合元素,使用“map[key]”访问字典元素;

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

注:集合元素访问是通过Iterator遍历来定位元素位置的

四、列表,字典,数组元素修改:可以使用赋值表达式或Expression接口的setValue方法修改;

//1.修改数组元素值
int[] array = new int[] {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);
//2.修改集合值
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);
//3.修改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);

对数组修改直接对“#array[index]”赋值即可修改元素值,同理适用于集合和字典类型。

五、集合投影:在SQL中投影指从表中选择出列,而在SpEL指根据集合中的元素中通过选择来构造另一个集合,该集合和原集合具有相同数量的元素;SpEL使用“(list|map).![投影表达式]”来进行投影运算:

//1.首先准备测试数据
Collection<Integer> collection = new ArrayList<Integer>();
collection.add(4); collection.add(5);
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("a", 1); map.put("b", 2);
//2.测试集合或数组
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”可以省略。

//3.测试字典
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将不添加到新集合中。

//1.首先准备测试数据
Collection<Integer> collection = new ArrayList<Integer>();
collection.add(4); collection.add(5);
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("a", 1); map.put("b", 2);
//2.集合或数组测试
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());

对于集合或数组选择,如“#collection.?[#this>4]”将选择出集合元素值大于4的所有元素。选择表达式必须返回布尔类型,使用“#this”表示当前元素。

//3.字典测试
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.?[#this.key != 'a']”将选择键值不等于”a”的,其中选择表达式中“#this”是Map.Entry类型,而最终结果还是Map,这点和投影不同;集合选择和投影可以一起使用,如“#map.?[key != 'a'].![value+1]”将首先选择键值不等于”a”的,然后在选出的Map中再进行“value+1”的投影。

5.3.4  表达式模板

模板表达式就是由字面量与一个或多个表达式块组成。每个表达式块由“前缀+表达式+后缀”形式组成,如“${1+2}”即表达式块。在前边我们已经介绍了使用ParserContext接口实现来定义表达式是否是模板及前缀和后缀定义。在此就不多介绍了,如“Error ${#v0} ${#v1}”表达式表示由字面量“Error ”、模板表达式“#v0”、模板表达式“#v1”组成,其中v0和v1表示自定义变量,需要在上下文定义。

Spring表达式语言 之 5.3 SpEL语法(拾肆)的更多相关文章

  1. 开涛spring3(5.3) - Spring表达式语言 之 5.3 SpEL语法

    5.3  SpEL语法 5.3.1  基本表达式 一.字面量表达式: SpEL支持的字面量包括:字符串.数字类型(int.long.float.double).布尔类型.null类型. 类型 示例 字 ...

  2. Spring表达式语言:SpEL

    1.Spring表达式语言(简称:SpEL):是一个支持运行时查询和操作对象图的强大的表达式语言. 2.语法类似于EL:SpEL使用#{...}作为定界符,所有在大括号内的字符都被认为是SpEL. 3 ...

  3. Spring表达式语言 之 5.1 概述 5.2 SpEL基础(拾叁)

    5.1  概述 5.1.1  概述 Spring表达式语言全称为"Spring Expression Language",缩写为"SpEL",类似于Struts ...

  4. 开涛spring3(5.1&5.2) - Spring表达式语言 之 5.1 概述 5.2 SpEL基础

    5.1  概述 5.1.1  概述 Spring表达式语言全称为“Spring Expression Language”,缩写为“SpEL”,类似于Struts2x中使用的OGNL表达式语言,能在运行 ...

  5. Spring表达式语言SpEL

    Spring表达式语言,简称SpEL,是一个支持运行时查询和操作对象图的强大的表达式语言.语法类似于EL:SpEL使用#{…}作为定界符,所有在大括号中的字符都将被认为是SpEL SpEL为bean属 ...

  6. Spring表达式语言之SpEL

    •Spring 表达式语言(简称SpEL):是一个支持运行时查询和操作对象图的强大的表达式语言. •语法类似于 EL:SpEL 使用 #{…} 作为定界符,所有在大框号中的字符都将被认为是 SpEL ...

  7. Spring基础13——Spring表达式语言:SpEL

    1.SpEL简介 Spring表达式语言(简称SpEL):是一个支持运行时查询和操作对象图的强大的表达式语言.语法上类似于EL:SpEL使用#{...}作为界定符,所有在大框号中的字符都将被认为是Sp ...

  8. Spring学习笔记--Spring表达式语言SpEL

    Spring3引入了Spring表达式语言(Spring Expression Language,SpEL).SpEL是一种强大的.简洁的装配Bean的方式,它通过运行期执行的表达式将值装配到Bean ...

  9. Spring表达式语言SpEL简单介绍

    Spring3引入了Spring表达式语言(Spring Expression Language,SpEL). SpEL有非常多特性.比較经常使用的包含: 1.使用bean的id来引用bean, 以下 ...

随机推荐

  1. for循环以及复杂if else 例题

    for循环语句 例一. 请输入一个整数,求1加到这个整数的和 例二 请从1到100 以内和7 有关的数字 例三,请设计一个游戏,前20关每关的分数是本身,21到30 是10分每关.31到40是20分每 ...

  2. ContentProvider官方教程(8)自定义MIME

    MIME Type Reference Content providers can return standard MIME media types, or custom MIME type stri ...

  3. CodeForces 146A Lucky Ticket

    Lucky Ticket Time Limit:2000MS     Memory Limit:262144KB     64bit IO Format:%I64d & %I64u Submi ...

  4. HDU 4870 Rating 概率DP

    Rating Time Limit:5000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Submit Statu ...

  5. FZU 2146

    Easy Game Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Submit St ...

  6. C# POST与Get数据

    引用DLL 普通Get数据和Post数据 public static string Get(string URL) { String ReCode = string.Empty; try { Http ...

  7. Zookeeper未授权访问

    l 漏洞名称: zookeeper未授权访问 l  漏洞影响版本: zookeeper l  漏洞细节: ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubb ...

  8. log4j的使用(1) —— 简单入门篇

    这里会介绍三种打印日志的方法:控制台Console,文件File,数据库DataBase 1.下载lo4j的jar包并导入project 2.因为要在数据库添加日志,所以先新建一个库,并新建打印日志的 ...

  9. NoSQL聚合数据模型

    NoSQL聚合数据模型 特点 聚合数据模型的特点就是把经常访问的数据放在一起(聚合在一块): 这样带来的好处很明显,对于某个查询请求,能够在与数据库一次交互中将所有数据都取出来: 当然,以这种方式存储 ...

  10. Object-c:两种文件读写的对比

    一.读写方法对比:(主要针对本地读取本地文件) 方式\操作 读 写 非URL方式 stringWithContentsOfFile writeToFile URL方式 stringWithConten ...