23 SpEl
23.1 简介
Spring也有自己的EL,叫Spring Expression Language,简称SpEl。其可以在程序中单独使用,也可以在Spring应用中进行bean定义时使用。其核心是org.springframework.expression.Expression接口,Spring使用该接口来表示EL中的表达式。通过Expression接口的系列getValue()方法我们可以获取对应Expression在特定EvaluationContext下的值,也可以通过其系列setValue()方法来设值。对应的Expression通常不是由我们直接来new对应实现类的实例,而是通过Spring提供的org.springframework.expression.ExpressionParser接口的系列parseExpression()方法来将一个字符串类型的表达式解析为一个Expression。以下是一个简单的示例,在该示例中我们将字符串表达式“1+2”解析为一个Expression,然后进行计算得出其值为3。
@Test
public void test() {
String expressionStr = "1+2";
ExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression(expressionStr);
Integer val = expression.getValue(Integer.class);
System.out.println(expressionStr + "的结果是:" + val);
}
Expression接口有一系列的getValue()方法,当其不接收任何参数时表示将会把Expression的计算结果当做一个Object进行返回,如果我们希望返回的是特定的类型,则可以传递对应的类型作为getValue()方法的参数,如上述示例中传递的Interger.class。我们也可以通过给Expression的getValue()方法传递EvaluationContext用以获取在特定环境下的计算结果,也可以传递一个Object作为Expression计算的rootObject。关于Expression接口的更多介绍请参考Spring的API文档。
23.2 示例
SpEl可以支持一般的算术运算,也可以支持逻辑运算,还可以支持对象的方法调用等。下面我们来看一些对应用法的示例。
23.2.1 算术运算
SpEl支持的算术运算可以是加、减、乘、除、求余、指数等。下面是一个对应的示例。
@Test
public void test01() {
ExpressionParser parser = new SpelExpressionParser();
Assert.assertTrue(parser.parseExpression("(1+2)*5 + 8-6/2").getValue().equals(20));//加减乘除
Assert.assertTrue(parser.parseExpression("8%3").getValue().equals(2));//求余
Assert.assertTrue(parser.parseExpression("2.0e3").getValue().equals(2000.0));//指数
Assert.assertTrue(parser.parseExpression("2^3").getValue().equals(8));//指数
}
23.2.2 逻辑运算
逻辑运算就是我们熟悉的与、或、非,在SpEl中就对应“and”、“or”和“!”。
@Test
public void test02() {
ExpressionParser parser = new SpelExpressionParser();
Assert.assertTrue(parser.parseExpression("true and true").getValue(Boolean.class));//与
Assert.assertTrue(parser.parseExpression("true or false").getValue(Boolean.class));//与
Assert.assertTrue(parser.parseExpression("!false").getValue(Boolean.class));//非
}
23.2.3 比较运算
比较运算就是我们熟悉的大于(>)、大于等于(>=)、小于(<)、小于等于(<=)、等于(==)和不等于(!=)。
@Test
public void test03() {
ExpressionParser parser = new SpelExpressionParser();
Assert.assertTrue(parser.parseExpression("5>3").getValue(Boolean.class));
Assert.assertTrue(parser.parseExpression("5<=8").getValue(Boolean.class));
Assert.assertTrue(parser.parseExpression("5==5").getValue(Boolean.class));
Assert.assertTrue(parser.parseExpression("5!=6").getValue(Boolean.class));
}
23.2.4 字符串
SpEl允许我们在表达式中直接使用int、double、String等。我们的Expression可以通过对应的字符串进行解析,那么当我们的表达式就是需要表示一个字符串时应该如何表示呢?这个时候需要通过单引号“’”来进行包裹。而当我们的字符串中包含单引号时,那么对应的单引号需要使用一个单引号进行转义,即连续两个单引号。
@Test
public void test04() {
ExpressionParser parser = new SpelExpressionParser();
Assert.assertTrue(parser.parseExpression("'abc'").getValue().equals("abc"));
Assert.assertTrue(parser.parseExpression("'''abc'").getValue().equals("'abc"));
}
23.2.5 访问方法
在SpEl表达式中我们也可以直接访问对象的方法。在下述示例中我们就直接在SpEl中访问了字符串的length()方法。
@Test
public void test05() {
ExpressionParser parser = new SpelExpressionParser();
//直接访问String的length()方法。
Assert.assertTrue(parser.parseExpression("'abc'.length()").getValue().equals(3));
}
23.2.6 使用EvaluationContext
我们先来看一个例子,在下列示例中我们在表达式中直接写name和getName(),这个时候Expression是无法解析的,因为其不知道name和getName()对应什么意思。
@Test
public void test06() {
ExpressionParser parser = new SpelExpressionParser();
parser.parseExpression("name").getValue();
parser.parseExpression("getName()").getValue();
}
通过指定EvaluationContext我们就可以让name和getName()变得有意义。指定了EvaluationContext之后,Expression将根据对应的EvaluationContext来进行解析。如下示例中我们构建了一个基于user对象的EvaluationContext,user对象将作为StandardEvaluationContext的rootObject,那么对应的Expression就将根据该rootObject对象来获取对应表达式的值。我们可以看到user对象定义了一个getName()方法,在解析name和getName()表达式时都将访问user对象的getName()方法,即它们的返回结果都为字符串“abc”。
@Test
public void test06() {
Object user = new Object() {
public String getName() {
return "abc";
}
};
EvaluationContext context = new StandardEvaluationContext(user);
ExpressionParser parser = new SpelExpressionParser();
Assert.assertTrue(parser.parseExpression("name").getValue(context, String.class).equals("abc"));
Assert.assertTrue(parser.parseExpression("getName()").getValue(context, String.class).equals("abc"));
}
上述示例中的表达式name表示对应EvaluationContext的rootObject的一个属性,在进行解析时,如果对应的get方法存在,则将直接访问对应的get方法,如上述示例中的getName(),否则将直接对其进行访问,这个时候就需要我们的属性是公有的,以便外部类可以访问。
对于对象而言,我们可以访问其属性的属性或方法,中间以点进行连接。
23.2.7 使用rootObject
当我们的表达式是基于某一个对象的时,我们也可以把对应的对象作为一个rootObject传递给对应的Expression以进行取值。如上述示例我们也可以直接将user对象作为rootObject传递给对应的Expression以获取对应的值。
@Test
public void test07() {
Object user = new Object() {
public String getName() {
return "abc";
}
};
ExpressionParser parser = new SpelExpressionParser();
Assert.assertTrue(parser.parseExpression("name").getValue(user, String.class).equals("abc"));
Assert.assertTrue(parser.parseExpression("getName()").getValue(user, String.class).equals("abc"));
}
23.2.8 List、Array、Map等元素的访问
在SpEl中我们可以通过索引的形式访问List或Array的某一个元素,对应的索引是从0开始的,以“list[index]”的形式出现。如下述示例中的test08_1和test08_2。
@Test
public void test08_1() {
Object user = new Object() {
public List<String> getInterests() {
List<String> interests = Arrays.asList(new String[] {"BasketBall", "FootBall"});
return interests;
}
};
ExpressionParser parser = new SpelExpressionParser();
Assert.assertTrue(parser.parseExpression("interests[0]").getValue(user, String.class).equals("BasketBall"));
Assert.assertTrue(parser.parseExpression("interests[1]").getValue(user, String.class).equals("FootBall"));
}
@Test
public void test08_2() {
Object user = new Object() {
public String[] getInterests() {
return new String[] {"BasketBall", "FootBall"};
}
};
ExpressionParser parser = new SpelExpressionParser();
Assert.assertTrue(parser.parseExpression("interests[0]").getValue(user, String.class).equals("BasketBall"));
Assert.assertTrue(parser.parseExpression("interests[1]").getValue(user, String.class).equals("FootBall"));
}
而对于Map而言,则是通过类似于“map[key]”的形式访问对应的元素的。示例如下。
@Test
public void test08_3() {
Object user = new Object() {
public Map<String, String> getInterests() {
Map<String, String> interests = new HashMap<String, String>();
interests.put("key1", "BasketBall");
interests.put("key2", "FootBall");
return interests;
}
};
ExpressionParser parser = new SpelExpressionParser();
Assert.assertTrue(parser.parseExpression("interests['key1']").getValue(user, String.class).equals("BasketBall"));
Assert.assertTrue(parser.parseExpression("interests['key2']").getValue(user, String.class).equals("FootBall"));
}