第四章节介绍Spring的EL表达式。内容大致分为两个部分
- 第一部分为与SpringEL相关的对象
- 第二部分为EL表达式。
1、核心对象
核心对象有以下几个:
- ExpressionParser:EL表达式解析器,它的主要职责是解析EL表达式
- Expression:当解析器解析EL表达式之后,会将结果保存在该对象中,调用对象的getValue方法获取解析的结果。
- EvaluationContext:解析的上下文,当EL表达式中存在变量,属性,方法时,根据上下文解析这些变量和方法。当为上下文中的变量时,当为JVM属性或者是容器中存在的properties属性时,当为容器中某个Bean的字段时,当为容器中对象的方法时,都会去上下文中查询相应的变量。如果只解析字面量,基本上用不到。
- SpelParserConfiguration:TODO,待补充。
2、EL表达式
我把EL表达式分为以下四类,
- 表达式为XX expression
- 运算符为XX operator
- 集合为Map,List,数组等相关的操作。
- 方法为调用构造器,调用类方法
2.1 表达式
表达式为XX expression,例如Literal expression,赋值,访问属性等表达式。
2.1.1 字面量
字面量有字符串,数字,布尔,null。
/** * * @Title: testLiteralExpression * @Description: 测试SpringEL表达式 */ public static void testLiteralExpression() { // 创建解析器 ExpressionParser parser = new SpelExpressionParser(); // 表达式 Expression exp = parser.parseExpression("'Hello World'"); // 字符串 String helloWorld = exp.getValue(String.class); // 获取数字 exp = parser.parseExpression("123"); // 数字 Integer num = exp.getValue(Integer.class); // 布尔值 boolean blValue = parser.parseExpression("true").getValue(Boolean.class); // null Object nullVal = parser.parseExpression("null").getValue(); System.out.println("HelloWorld:" + helloWorld + " num:" + num + " blValue:" + blValue + " nullVal:" + nullVal); }
2.1.2 Bean属性
当获取对象的属性时,需要在上下文中明确对象,体现在代码上,getValue的第二个参数为对象的实例。
例如获取User的name属性
/** * * @Title: testBeanFields * @Description: 测试获取bean的属性 */ public static void testBeanFields() { // 创建解析器 ExpressionParser parser = new SpelExpressionParser(); // 表达式 Expression exp = parser.parseExpression("name"); // 创建User对象 User user = new User(); user.setName("张三"); // 获取name属性,第一个参数为对象的实例,第二个参数为name属性的类型 String name = exp.getValue(user, String.class); System.out.println("name:" + name); }
当属性为对象时,例如User有Address(地址)属性,address.country,表示用户所在的国家,即Address对象中的country属性。
当属性为集合,数组时,例如User有List<Role>属性,roles[index]访问到Role对象,roles[index].name获取Role对象的角色名称。
当属性为Map时,其中的index转换为key。
2.1.3 系统属性
系统属性是指JVM属性和操作系统的属性。获取系统属性的格式为:#{systemProperties[key]}
其中
- #:表示变量
- {}:变量包裹在大括号中
- systemProperties:它代表系统属性。
- key:系统属性变量的key值。
例如获取user.name系统变量
public class User { // 用户的名称 @Value("#{ systemProperties['user.name'] }") private String name; // 其他代码省略 }
当从IOC容器中获取时,name的属性值会是user.name系统变量值。使用new方式自定义User对象时,name属性无值。
表达式也可以出现在XML配置文件中,当使用property子标签设置name属性时,当value为表达式的值时,也会被解析为user.name系统变量值。
2.1.4 表达式模板
表达式模板是指引用上下文中对象的属性,调用上下文中对象的方法,获取它返回值等等。
// 任意的表达式模板,必须把User设置为上下文对象,并且User的name属性有值 Expression exp = parser.parseExpression("your name is #{name} ");
返回字符串“your name is 张三”。
2.2 运算符
运算符有五种,关系运算符,逻辑运算符,算术运算符,赋值运算符,三目运算符。
2.2.1 关系运算符
关系运算符,大于,等于,小于等等,这些值返回的都是布尔类型。
/** * * @Title: testRelationalOperators * @Description: 测试关系运算符 */ public static void testRelationalOperators() { // 创建解析器 ExpressionParser parser = new SpelExpressionParser(); // 任意的关系运算符 Expression exp = parser.parseExpression("1 < 2"); // 获取值 Boolean isTrue = exp.getValue(Boolean.class); // 关系运算符 System.out.println("relational expression:" + isTrue); }
2.2.2 逻辑运算符
逻辑运算符有与,或,非三种,Spel使用and,or,not表示。返回的值也都是布尔类型
// 任意的逻辑运算符 Expression exp = parser.parseExpression("true and false");
2.2.3 算术运算符
算术运算符有加,减,乘,除,余,各种函数等。返回的基本都是数字类型
// 任意的算术运算符 Expression exp = parser.parseExpression("1 + 1");
2.2.4 赋值运算符
赋值运算符有=,+=,-=等等。
// 任意的赋值运算符,value是字符串,需要加单引号 Expression exp = parser.parseExpression(" key=’value’ ");
还可以调用expression的setValue方法。
// 任意的赋值运算符,field是某个对象的属性 Expression exp = parser.parseExpression(" field "); // setValue方法,第一个参数为上下文对象,第二个参数为对象实例,第三个为filed属性的值 exp.setValue(context, inventor, "fieldVal");
2.2.5 三目运算符
格式与Java程序中的三目运算符一致。
exp ? ‘true value’ :’false value’。
// 任意的三目表达式,值是字符串,需要添加单引号 Expression exp = parser.parseExpression(" true ? 'true value' : 'false value' ");
2.2.6 Elvis 运算符
它是三目运算符的缩写,是Groovy语言的语法。
表达式一般是判断对象或字段是否为空,true value 一般是对象或字段本身,false value一般是对象或字段的默认值。
格式为: obj ? new Object() 或 field ? “default field Value”;
// 任意的Elvis运算符,相当于name属性无值时,给其设置默认值张三 Expression exp = parser.parseExpression(" name ? : '张三' ");
2.2.7 Safe navigation运算符
它是在获取对象的属性时,添加一层机制,确保即使对象为空时,也不会抛出空指针异常,相当于获取对象属性的加强版。
格式为:obj ?. field。 中间是问号和逗号,二者之间无空格。
// 任意的获取属性的运算符,获取User对象中的address对象,address对象中的country属性值 Expression exp = parser.parseExpression(" address?.country ");
2.3 集合
集合这里指存放数据的容器,指数组,Collection,Map。
2.3.1 创建
- List集合:格式为{value1,value2,value3},大括号可以互相嵌套。返回List对象
// {value1,value2,value3}等格式,大括号可以互相嵌套 Expression exp = parser.parseExpression("{1,2,3,4}");
2.Map:格式为{key:’value’},相当于JSON字符串。Key值没有单引号,value上有单引号
// 获取时返回Map对象 Expression exp = parser.parseExpression("{name:'张三', age:18}");
3.数组:格式为Java创建数组的格式,new type[length]
// 创建int数组,大小为4,前两个的值为1,2,后面两个使用默认值0 Expression exp1 = parser.parseExpression("new int[4]{1,2}");
2.3.2 遍历
遍历集合的格式为 coll.?[boolean exp],相当于JS数组的filter方法。其中
- Coll:表示遍历的集合
- .?:逗号和问号为固定格式。
- Exp:将当前遍历的对象作为上下文,编写过滤条件,例如[name == ‘张三’],遍历用户的集合,返回姓名为张三的用户。
当为map类型时,方括号内部使用key表示当前遍历到的键值,使用value表示当前遍历到的value值。
当只想获取集合中满足条件的第一个值时,coll.^[Boolean exp],
当只想获取集合中满足条件的最后一个值是,coll.$[Boolean exp]。
2.3.3 投影
集合投影的格式为coll.![projection expression],相当于JS数组的map方法。其中
- Coll:表示遍历的集合
- .! :逗号和感叹号为固定格式。
- Projection expression:将当前遍历的对象作为上下文,编写投影的表达式,例如[‘test’ + name],遍历当前用户,返回当前用户的用户名集合,并且在每个用户名上添加test前缀。
2.3.4 引用上下文
无论是上述提到的遍历,投影操作,都可以通过 #this引用到当前遍历的对象,通过#root访问到上下文对象,例如Role对象有List<User>,那么#root会引用到role对象,#this会引用到当前遍历的User对象。
2.4 方法
2.4.1 构造器
在Spel中调用构造器,需要new classFullName,即类全名(包名+类名)。
2.4.2 对象方法
在Spel中调用对象方法时,必须将对象添加到上下文中。
// 调用User对象的getName方法,返回'your name is 张三' Expression exp = parser.parseExpression("your name is getName()");