Spring Expression Language学习文档(5.1.0.RELEASE版本)

1. 简介

Spring Expression Language(SpEL,Spring表达式语言)是一种强大的支持运行时查询和操作对象导图功能的表达式语言。它的语法类似于传统的表达式语言,但在此基础上提供更多的功能,例如:函数调用和基本的字符串模板方法。

2. 功能概述

Spring表达式语言支持以下功能:

  • Literal expressions - 文字表达式
  • Boolean and relational operators - 布尔和关系运算符
  • Regular expressions - 正则表达式
  • Class expressions - 类表达式
  • Accessing properties, arrays, lists and maps - 访问属性、数组、列表、maps
  • Method invocation - 方法调用
  • Relational operators - 关系运算符
  • Assignment - 分配
  • Calling constructors - 调用构造器
  • Bean references - Bean引用
  • Array construction - 构造数组
  • Inline lists - 内嵌列表
  • Inline maps - 内嵌maps
  • Ternary operator - 三元运算符
  • Variables - 变量
  • User-defined functions - 用户自定义的方法
  • Collection projection - 集合投影
  • Collection selection - 集合筛选
  • Templated expression - 模板表达式

3. SpEL的类和接口简介

SpEL的类和接口位于org.springframework.expression包及其子包中,例如:spel.support。

3.1 ExpressionParser接口

ExpressionParser接口用来解析表达式字符串。

  • Expression ExpressionParser.parseExpression(String expressionStr) 方法将表达式字符串进行转换,返回一个表达式类型(Expression)的变量。

3.2 Expression接口

Expression接口用来计算表达式字符串。

  • Object Expression.getValue()方法将计算或执行表达式,并返回一个一个Object类。

3.3 异常

当ExpressionParser.parseExpression(String expressionStr)和Expression.getValue(…)被调用的时候,ParseException和EvaluationException将分别被抛出。

3.4 EvaluationContext接口

EvaluationContext接口在计算表达式时被用来处理属性、方法、域,以及执行类型转换。 Spring对该接口提供了两种实现:

  • SimpleEvaluationContext类:公开了一部分基本的SpEL语言功能和配置选项的子集,用于不不需要所有类型的Spring表达式,即:Spring表达式的类型是被有目的的限制。它的应用实例包括但不限于:数据绑定类型的表达式、基于属性的过滤器等。
  • StandardEvalationContext类:公开全套SpEL语言功能和配置选项。 可以使用它来指定默认根对象并配置每个可用的与计算表达式相关的策略。
    其中SimpleEvaluationContext是设计来提供Spring表达式语言句法的子集。它排除了java类型的引用、构造器以及bean的引用。它还要求使用者明确选择表达式中属性和方法的支持级别。 默认情况下,create()静态工厂方法仅启用对属性的读访问权限。 除此外还可以用构建器(builder)来配置所需的确切支持级别,定位以下一个或多个组合:
    • 非反射的常规PropertyAccessor
    • 只读的数据绑定属性(data binding properties for read-only access)
    • 可读写的数据绑定属性(data binding properties for read and write)

3.5 类型转换

当SpEL可以获取和设置表达式中对象的值。在设置的时候,可能会发生类型转换,Spring表达式是支持这样的类型转换的。例如:

class Simple {
    public List<Boolean> booleanList = new ArrayList<Boolean>();
}

Simple simple = new Simple();
simple.booleanList.add(true);

EvaluationContext context = SimpleEvaluationContext().forReadOnlyDataBinding().build();

// false is passed in here as a string. SpEL and the conversion service
// correctly recognize that it needs to be a Boolean and convert it
parser.parseExpression("booleanList[0]").setValue(context, simple, "false");

// b is false
Boolean b = simple.booleanList.get(0);

3.6 解析器配置

可以使用解析器配置对象(org.springframework.expression.spel.SpelParserConfiguration)配置SpEL表达式解析器。 配置对象控制某些表达式组件的行为。 例如,如果索引到数组或集合,并且指定索引处的元素为null,则可以自动创建该元素。 当使用由一系列属性引用组成的表达式时,这很有用。 如果索引到数组或列表并指定超出数组或列表当前大小末尾的索引,则可以自动增大数组或列表以适应该索引。 以下示例演示通过SpelParserConfiguration类配置自动增长列表:

class Demo {
    public List<String> list;
}

//Configuration turns on:
// - auto null reference initialization
// - auto collection growing
SpelParserConfiguration config = new SpelParserConfiguration(true,true);

ExpressionParser parser = new SpelExpressionParser(config);

Expression expression = parser.parseExpression("list[3]");

Demo demo = new Demo();

Object o = expression.getValue(demo);

// demo.list will now be a real collection of 4 entries
// Each entry is a new empty String

4. SpEL编译器

Spring Framework 4.1包含一个基本的表达式编译器。表达式通常是被解释执行的,以在计算/执行期间提供很多动态灵活性,但是却导致不能提供最佳性能。对于偶尔的表达式使用,这很好,但是,当其他组件(如Spring Integration)使用时,性能可能非常重要,并且不需要动态。
SpEL编译器旨在满足此需求。在评估期间,编译器生成一个真实的Java类,它体现了表达式行为,并使用它来实现更快的表达式计算/执行。由于缺少表达式类型,编译器编译时使用解释执行的方式去收集信息。例如,编译器不能完全从表达式中知道属性引用的类型,但是,在第一次解释执行时,编译器能够获取起类型信息。当然,如果各种表达元素的类型随时间变化,那么基于此信息的编译可能会导致问题。因此,编译器最适合用于在重复计算/执行类型信息不会改变的表达式。

4.1 编译器配置

可以使用org.springframework.expression.spel.SpelCompilerMode枚举类来配置编译器:

  • OFF(默认):编译器关闭。
  • IMMDIATE:在IMMDIATE模式下,表达式会尽快编译。 这通常是在第一次解释执行之后。 如果编译的表达式失败(通常是由于类型更改),将会抛出异常。
  • MIXED:在混合模式下,表达式会随着时间的推移在解释和编译模式之间静默切换。 经过一定数量的解释运行后,它们会切换到编译形式,如果编译后出现问题(如类型更改),表达式会自动再次切换回解释形式。 稍后,它可能会再切换到编译模式。 类似于IMMEDIATE模式的异常将在内部处理。

4.2 编译器限制

从Spring框架4.1版本开始,Spring就开始提供基本的编译框架。然而,该框架并没能支持所有表达式的编译。编译框架最初的目的是编译在关键上下文中经常使用的通用表达式。目前,以下几种表达式不能被编译:

  • 涉及赋值的表达式
  • 依托转换服务的表达式
  • 使用自定义解析器或访问器的表达式
  • 使用选择或投影的表达式

5. 在Bean Definitions中的表达式

5.1 XML配置

属性和构造器参数的值可以使用表达式来设置,例如:

<bean id="numberGuess" class="org.spring.samples.NumberGuess">
    <property name="randomNumber" value="#{ T(java.lang.Math).random() * 100.0 }"/>

    <!-- other properties -->
</bean>

5.2 注释配置

可以使用在域、方法或狗在其参数前使用@Value注释来指定一个默认的值。例如:

5.2.1 域变量

public static class FieldValueTestBean {

    @Value("#{ systemProperties['user.region'] }")
    private String defaultLocale;

    public void setDefaultLocale(String defaultLocale) {
        this.defaultLocale = defaultLocale;
    }

    public String getDefaultLocale() {
        return this.defaultLocale;
    }

}

5.2.2 属性setter方法

public static class PropertyValueTestBean {
    private String defaultLocale;

    @Value("#{ systemProperties['user.region'] }")
    public void setDefaultLocale(String defaultLocale) {
        this.defaultLocale = defaultLocale;
    }

    public String getDefaultLocale() {
        return this.defaultLocale;
    }

}

5.2.3 方法

public class SimpleMovieLister {

    private MovieFinder movieFinder;
    private String defaultLocale;

    @Autowired
    public void configure(MovieFinder movieFinder,
            @Value("#{ systemProperties['user.region'] }") String defaultLocale) {
        this.movieFinder = movieFinder;
        this.defaultLocale = defaultLocale;
    }

    // ...
}

5.2.4 构造器

public class MovieRecommender {

    private String defaultLocale;

    private CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    public MovieRecommender(CustomerPreferenceDao customerPreferenceDao,
            @Value("#{systemProperties['user.country']}") String defaultLocale) {
        this.customerPreferenceDao = customerPreferenceDao;
        this.defaultLocale = defaultLocale;
    }

    // ...
}

6. 语言引用(Language Reference)

6.1 Literal expressions - 文字表达式

文字表达式支持字符串、数值(int, real, hex)、布尔型和空这几种类型。字符串放在单引号中。如果在字符串中有单引号字符,则使用两个单引号字符来表示。
例如:

ExpressionParser parser = new SpelExpressionParser();

// evals to "Hello World"
String helloWorld = (String) parser.parseExpression("'Hello World'").getValue();

double avogadrosNumber = (Double) parser.parseExpression("6.0221415E+23").getValue();

// evals to 2147483647
int maxValue = (Integer) parser.parseExpression("0x7FFFFFFF").getValue();

boolean trueValue = (Boolean) parser.parseExpression("true").getValue();

Object nullValue = parser.parseExpression("null").getValue();

SimpleEvaluationContext并未限制文字类的表达式

6.2 Accessing properties, arrays, lists, maps and Indexers - 访问属性、数组、列表、maps、Indexers

6.2.1 属性

Spring表达式可允许用户通过表达式访问指定对象的属性,SimpleEvaluationContext并未限制通过表达式访问对象的属性。例如:

ExpressionParser parser = new SpelExpressionParser();

GregorianCalendar calendar = new GregorianCalendar();
calendar.set(1856, 7, 9);
Inventor inventor = new Inventor("Alice Miller", calendar.getTime(), "America");
PlaceOfBirth placeOfBirth = new PlaceOfBirth("NewYork", "America");
inventor.setPlaceOfBirth(placeOfBirth);

EvaluationContext simpleEvaluationContext = SimpleEvaluationContext.forReadWriteDataBinding().withRootObject(inventor).build();

int year = (Integer) parser.parseExpression("birthdate.Year + 100").getValue(simpleEvaluationContext);
System.out.println(year);

String city = (String) parser.parseExpression("placeOfBirth.city").getValue(simpleEvaluationContext);
System.out.println(city);

6.2.2 数组

Spring表达式可允许用户通过表达式访问指定数组,SimpleEvaluationContext并未限制通过表达式访问数组。例如:

ExpressionParser parser = new SpelExpressionParser();
Inventor inventor = new Inventor();

String[] inventions = {"telephone","cap","cellphone","desktop"};
inventor.setInventions(inventions);

EvaluationContext simpleEvaluationContext = SimpleEvaluationContext.forReadWriteDataBinding().build();

String invention = parser.parseExpression("inventions[3]").getValue(simpleEvaluationContext, inventor, String.class);
System.out
.println(invention);
ExpressionParser parser = new SpelExpressionParser();
Inventor inventor = new Inventor();

String[] inventions = {"telephone","cap","cellphone","desktop"};
inventor.setInventions(inventions);

EvaluationContext simpleEvaluationContext = SimpleEvaluationContext.forReadWriteDataBinding().build();

String invention = parser.parseExpression("inventions[3]").getValue(simpleEvaluationContext, inventor, String.class);
System.out.println(invention);

6.2.3 List

Spring表达式可允许用户通过表达式访问指定List,SimpleEvaluationContext并未限制通过表达式访问List。例如:

ExpressionParser parser = new SpelExpressionParser();
EvaluationContext simpleEvaluationContext = SimpleEvaluationContext.forReadWriteDataBinding().build();
Society ieee = new Society();

GregorianCalendar calendar = new GregorianCalendar();
calendar.set(1856, 7, 9);
Inventor inventor = new Inventor("Alice Miller", calendar.getTime(), "America");

ieee.addMember(inventor);

String name = parser.parseExpression("Members[0].Name").getValue(simpleEvaluationContext, ieee, String.class);
System.out.println(name);

6.2.4 Map

Spring表达式可允许用户通过表达式访问Map,SimpleEvaluationContext并未限制通过表达式访问Map。例如:

ExpressionParser parser = new SpelExpressionParser();
EvaluationContext simpleEvaluationContext = SimpleEvaluationContext.forReadWriteDataBinding().build();
Map officers = new HashMap<String ,Inventor>();

GregorianCalendar calendar = new GregorianCalendar();
calendar.set(1856, 7, 9);
Inventor inventor = new Inventor("Alice Miller", calendar.getTime(), "America");

officers.put("president",inventor);
simpleEvaluationContext.setVariable("Officers",officers);

Inventor inventor1 = parser.parseExpression("#Officers['president']").getValue(simpleEvaluationContext, Inventor.class);
System.out.println(inventor1.getName());

6.3 Inline lists - 内嵌列表

在Spring表达式中,可以直接使用{}来表达一个列表,SimpleEvaluationContext并未限制通过表达式访问Inline lists。例如:

ExpressionParser parser = new SpelExpressionParser();
EvaluationContext simpleEvaluationContext = SimpleEvaluationContext.forReadWriteDataBinding().build();

java.util.List numbers = (java.util.List) parser.parseExpression("{1,2,3,4,5}").getValue(simpleEvaluationContext);
System.out.println(numbers.get(3)+"~");

java.util.List listOfLists = (java.util.List) parser.parseExpression("{{'a','b'},{'x','y'}}").getValue(simpleEvaluationContext);
System.out.println(((List)listOfLists.get(1)).get(1));

6.4 Inline maps - 内嵌maps

在Spring表达式中,可以使用{key:value},SimpleEvaluationContext并未限制通过表达式访问Inline Maps。例如:

ExpressionParser parser = new SpelExpressionParser();
EvaluationContext simpleEvaluationContext = SimpleEvaluationContext.forReadWriteDataBinding().build();

Map inventorInfo = (Map) parser.parseExpression("{name:'Nikola',dob:'10-July-1856'}").getValue(simpleEvaluationContext);
System.out.println(inventorInfo.get("name"));

Map mapOfMaps = (Map) parser.parseExpression("{name:{first:'Nikola',last:'Tesla'},dob:{day:10,month:'July',year:1856}}").getValue(simpleEvaluationContext);
System.out.println(((Map)mapOfMaps.get("dob")).get("day"));

6.5 Array construction - 构造数组

Spring表达式支持在表达式中构造数组,SimpleEvaluationContext并未限制通过表达式构造数组。例如:

ExpressionParser parser = new SpelExpressionParser();
EvaluationContext simpleEvaluationContext = SimpleEvaluationContext.forReadWriteDataBinding().build();
int[] numbers1 = (int[]) parser.parseExpression("new int[4]").getValue(simpleEvaluationContext);
numbers1[3] = 2;
for (int num : numbers1) {
    System.out.print(num);
    System.out.print(" ");
}
System.out.println();

// Array with initializer
int[] numbers2 = (int[]) parser.parseExpression("new int[]{1,2,3}").getValue(simpleEvaluationContext);
for (int num : numbers2) {
    System.out.print(num);
    System.out.print(" ");
}
System.out.println();

// Multi dimensional array
int[][] numbers3 = (int[][]) parser.parseExpression("new int[4][5]").getValue(simpleEvaluationContext);
numbers3[3][2] = 32;
for (int[] num_x:numbers3) {
    for
(int num : num_x) {
        System.out.print(num);
        System.out.print(" ");
    }
}
System.out.println();

6.6 Method invocation - 方法调用

Spring表达式支持在表达式中调用方法。
注意:SimpleEvaluationContext默认情况下是不允许调用方法的。但是提供了withInstanceMethods()和withMethodResolvers()方法来提供方法调用,但是仍然禁止了静态方法调用。
例如:

ExpressionParser parser = new SpelExpressionParser();
EvaluationContext simpleEvaluationContext = SimpleEvaluationContext.forReadWriteDataBinding().withInstanceMethods().build();

Society ieee = new Society();
GregorianCalendar calendar = new GregorianCalendar();
calendar.set(1856, 7, 9);
Inventor inventor = new Inventor("Alice Miller", calendar.getTime(), "America");
ieee.addMember(inventor);

boolean isMember = parser.parseExpression("isMember('Alice Miller')").getValue(simpleEvaluationContext,ieee,Boolean.class);
System.out.println(isMember);

String subStr = (String) parser.parseExpression("'abc'.substring(1,3)").getValue(simpleEvaluationContext);
System.out.println(subStr);

//Exception in thread "main" org.springframework.expression.spel.SpelEvaluationException: EL1004E: Method call: Method staticMethod() cannot be found on com.pow.study.spel.inventor.Society type
String invokeStr = (String) parser.parseExpression("staticMethod()").getValue(simpleEvaluationContext, ieee);
System.out.println(invokeStr);

6.7 运算符

6.7.1 Relational Operators - 关系运算符

Spring表达式支持关系运算符,SimpleEvaluationContext并未限制表达式中的关系运算符。例如:

ExpressionParser parser = new SpelExpressionParser();
EvaluationContext simpleEvaluationContext = SimpleEvaluationContext.forReadWriteDataBinding().build();

// evaluates to true
boolean boolValue1 = parser.parseExpression("2 == 2").getValue(simpleEvaluationContext, Boolean.class);
System.out.println(boolValue1);

// evaluates to false
boolean boolValue2 = parser.parseExpression("2 < -5.0").getValue(simpleEvaluationContext, Boolean.class);
System.out.println(boolValue2);

// evaluates to true
boolean boolValue3 = parser.parseExpression("'black' < 'block'").getValue(simpleEvaluationContext, Boolean.class);
System.out.println(boolValue3);

6.7.2 Logical Operators - 逻辑运算符

Spring表达式支持逻辑运算符(and, or, not),SimpleEvaluationContext并未限制表达式中的逻辑运算符。例如:

ExpressionParser parser = new SpelExpressionParser();
EvaluationContext simpleEvaluationContext = SimpleEvaluationContext.forReadWriteDataBinding().build();

// evaluates to false
boolean boolValue1 = parser.parseExpression("true and false").getValue(simpleEvaluationContext, Boolean.class);
System.out.println(boolValue1);

// evaluates to false
boolean boolValue2 = parser.parseExpression("!true").getValue(simpleEvaluationContext, Boolean.class);
System.out.println(boolValue2);

// evaluates to true
boolean boolValue3 = parser.parseExpression("true or false").getValue(simpleEvaluationContext, Boolean.class);
System.out.println(boolValue3);

6.7.3 Mathematical Operators - 数学运算符

Spring表达式支持数学运算符,SimpleEvaluationContext并未限制表达式中的数学运算符。例如:

ExpressionParser parser = new SpelExpressionParser();
EvaluationContext simpleEvaluationContext = SimpleEvaluationContext.forReadWriteDataBinding().build();

// Addition
int add = parser.parseExpression("2 + 2").getValue(simpleEvaluationContext, Integer.class);
System.out.println(add);

// Subtraction
int sub = parser.parseExpression("7 - 2").getValue(simpleEvaluationContext, Integer.class);
System.out.println(sub);

// Multiplication
int mul = parser.parseExpression("6 * 3").getValue(simpleEvaluationContext, Integer.class);
System.out.println(mul);

// Division
int div = parser.parseExpression("6 / 3").getValue(simpleEvaluationContext, Integer.class);
System.out.println(div);

// Modulus
int mod = parser.parseExpression("6 % 3").getValue(simpleEvaluationContext, Integer.class);
System.out.println(mod);

// Operator precedence
int result = parser.parseExpression("(3 + 5) / 2").getValue(simpleEvaluationContext, Integer.class);
System.out.println(result);

6.7.4 The Assignment Operators - 赋值运算符

Spring表达式支持在表达式中进行赋值操作,SimpleEvaluationContext并未限制表达式中的赋值操作。例如:

ExpressionParser parser = new SpelExpressionParser();
EvaluationContext simpleEvaluationContext = SimpleEvaluationContext.forReadWriteDataBinding().build();

int num = 8;
simpleEvaluationContext.setVariable("num", num);

// num = 9
num = (Integer) parser.parseExpression("#num = 9").getValue(simpleEvaluationContext);
System.out.println(num);

6.8 Types

T操作符用来指定一个java.lang.Class的实例(types)。静态方法也可以使用这个操作符来调用,例如:T(java.lang.Math).random()。
StandardEvaluationContext可以使用一个TypeLocator来找到指定的type,但是SimpleEvaluationContext不支持在表达式中使用T操作符来指定一个类。例如:

ExpressionParser parser = new SpelExpressionParser();
EvaluationContext simpleEvaluationContext = SimpleEvaluationContext.forReadWriteDataBinding().build();
EvaluationContext standardEvaluationContext = new StandardEvaluationContext();

//result : java.lang.Math
Class mathClass = parser.parseExpression("T(java.lang.Math)").getValue(standardEvaluationContext, Class.class);
System.out.println(mathClass.getName());

//Exception in thread "main" org.springframework.expression.spel.SpelEvaluationException: EL1005E: Type cannot be found 'java.util.Date'
Class dateClass = parser.parseExpression("T(java.util.Date)").getValue(simpleEvaluationContext, Class.class);
System.out.println(dateClass.getName());

6.9 Calling constructors - 调用构造器

Spring表达式支持在表达式中使用构造器来创建对象(new)。StandardEvaluationContext可以使用ConstructorResolvers来处理new操作,但是SimpleEvaluationContext不支持。例如:

ExpressionParser parser = new SpelExpressionParser();
EvaluationContext simpleEvaluationContext = SimpleEvaluationContext.forReadWriteDataBinding().build();
EvaluationContext standardEvaluationContext = new StandardEvaluationContext();

//result : Albert Einstein
Inventor inventor1 = parser.parseExpression("new com.pow.study.spel.inventor.Inventor('Albert Einstein', 'Germany')").getValue(standardEvaluationContext, Inventor.class);
System.out.println(inventor1.getName());

//Exception in thread "main" org.springframework.expression.spel.SpelEvaluationException: EL1002E: Constructor call: No suitable constructor found on type com.pow.study.spel.inventor.Inventor for arguments (java.lang.String,java.lang.String)
Inventor inventor2 = parser.parseExpression("new com.pow.study.spel.inventor.Inventor('Alice Miller', 'America')").getValue(simpleEvaluationContext, Inventor.class);
System.out.println(inventor2.getName());

6.10 Variables - 变量

Spring表达式支持在表达式中使用变量。SimpleEvaluationContext也同样支持这一功能。例如:

ExpressionParser parser = new SpelExpressionParser();
EvaluationContext simpleEvaluationContext = SimpleEvaluationContext.forReadWriteDataBinding().build();

Inventor inventor = new Inventor("Alice Miller", "America");
simpleEvaluationContext.setVariable("newName", "Nikola Tesla");

parser.parseExpression("Name = #newName").getValue(simpleEvaluationContext, inventor);
System.out.println(inventor.getName());

注意:

  • #this变量通常被定义来指向当前正在评估的对象,随着表达式被执行的变化而变化。
  • #root变量通常被定义来指向context的ObjectContext,不变。
    例如:
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext simpleEvaluationContext = SimpleEvaluationContext.forReadWriteDataBinding().build();
Inventor inventor = new Inventor("Alice Miller", "America”);

//com.pow.study.spel.inventor.Inventor
Object object = parser.parseExpression("#root").getValue(simpleEvaluationContext,inventor);
System.out.println(object.getClass().getName());

List<Integer> primes = new ArrayList<Integer>();
primes.addAll(Arrays.asList(2,3,5,7,11,13,17,19,23,29,31,37,41));
simpleEvaluationContext.setVariable("primes", primes);
//11, 13, 17, 19, 23, 29, 31, 37, 41]
List<Integer> primesGreaterThanTen = (List<Integer>) parser.parseExpression("#primes.?[#this>10]").getValue(simpleEvaluationContext);
System.out.println(primesGreaterThanTen);

6.11 Functions

Spring表达式支持用户调用已注册的静态函数。其中,StandardEvaluationContext支持setVariable()方法和registerFunction()方法注册静态函数,但是SimpleEvaluationContext()只支持setVariable()方法。例如:

ExpressionParser parser = new SpelExpressionParser();
EvaluationContext simpleEvaluationContext = SimpleEvaluationContext.forReadWriteDataBinding().withInstanceMethods().build();
StandardEvaluationContext standardEvaluationContext = new StandardEvaluationContext();

try {
    Method random = java.lang.Math.class.getDeclaredMethod("random");
    Method getRuntime = java.lang.Runtime.class.getDeclaredMethod("getRuntime");

    standardEvaluationContext.setVariable("random", random);
    standardEvaluationContext.registerFunction("getRuntime", getRuntime);

    simpleEvaluationContext.setVariable("random",random);
    simpleEvaluationContext.setVariable("getRuntime", getRuntime);

    //get a random
    Object o = parser.parseExpression("#random()").getValue(standardEvaluationContext);
    System.out.println(o);

    //open a calculator
    Object o1 = parser.parseExpression("#getRuntime().exec('open /Applications/Calculator.app')").getValue(standardEvaluationContext);
    System.out.println(o1);

    //get a random
    Object o2 = parser.parseExpression("#random()").getValue(simpleEvaluationContext);
    System.out.println(o2);

    //open a calculator
    Object o3 = parser.parseExpression("#getRuntime().exec('open /Applications/Calculator.app')").getValue(simpleEvaluationContext);
    System.out.println(o3);
} catch (NoSuchMethodException e) {
    System.out.println(e.getMessage());
}

6.12 Bean references - Bean引用

Spring 表达式支持BeanReference。StandardEvaluationContext允许用户使用setBeanResolver方法注册bean并进行调用,但SimpleEvaluationContext不支持这种类型的表达式

package com.pow.study.spel;

import org.springframework.expression.AccessException;
import org.springframework.expression.BeanResolver;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

/**
 * @author pow
 * @date 2018/10/10 上午10:39
 */
public class BeanReference {
    public void test() {
        ExpressionParser parser = new SpelExpressionParser();
        StandardEvaluationContext context = new StandardEvaluationContext();
        context.setBeanResolver(new MyBeanResolver());

        Object bean = parser.parseExpression("&foo").getValue(context);
        System.out.println(bean);
    }

    public static void main(String[] args) {
        BeanReference demo = new BeanReference();
        demo.test();
    }

    /**
     * @author pow
     * @date 2018/10/9 下午9:09
     */
    public static class MyBeanResolver implements BeanResolver {
        @Override
        public Object resolve(EvaluationContext context, String beanName) throws AccessException {
            if (beanName.equals("foo") || beanName.equals("bar")) {
                return "MyBeanResolver";
            }
            if (beanName.equals("&foo")) {
                return "MyBeanResolver-&foo";
            }
            throw new AccessException("not heard of " + beanName);
        }
    }
}

6.13 Ternary operator - 三元运算符(if-then-else)

Spring表达式支持IF-THEN-ELSE的三元运算符。SimpleEvaluationContext也支持该类表达式,例如:

ExpressionParser parser = new SpelExpressionParser();
EvaluationContext simpleEvaluationContext = SimpleEvaluationContext.forReadWriteDataBinding().build();

//falseExp
String string = parser.parseExpression("false ? 'trueExp' : 'falseExp'").getValue(simpleEvaluationContext, String.class);
System.out.println(string);

6.14 Elvis操作符

Spring表达式支持name?:”Unknown”(等价于:name == null ? name : “Unknown")类型的逻辑运算。SimpleEvaluationContext也支持该类表达式,例如:

ExpressionParser parser = new SpelExpressionParser();
EvaluationContext simpleEvaluationContext = SimpleEvaluationContext.forReadWriteDataBinding().build();

String name = "Elvis Presley";
simpleEvaluationContext.setVariable("name", name);

//Elvis Presley
String string = parser.parseExpression("#name?:'Unknown'").getValue(simpleEvaluationContext, String.class);
System.out.println(string);

6.15 Safe Navigation Operator

Safe Navigation Operator用来避免空指针异常。如果在调用某对象的某一属性时,如果对象为空,则返回空,而不是抛出异常。SimpleEvaluationContext也支持这一类表达式。例如:

ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();

Inventor tesla = new Inventor("Nikola Tesla", "Serbian");
tesla.setPlaceOfBirth(new PlaceOfBirth("Smiljan"));

String city = parser.parseExpression("PlaceOfBirth?.City").getValue(context, tesla, String.class);
System.out.println(city);  // Smiljan

tesla.setPlaceOfBirth(null);
city = parser.parseExpression("PlaceOfBirth?.City").getValue(context, tesla, String.class);
System.out.println(city);  // null - does not throw NullPointerException!!!

6.16 Collection selection - 集合筛选

筛选是表达式语言中的一个强大功能,Spring表达式支持此功能,且在SimpleEvaluationContext中也支持。例如:

ExpressionParser parser = new SpelExpressionParser();
EvaluationContext simpleEvaluationContext = SimpleEvaluationContext.forReadWriteDataBinding().build();
Society ieee = new Society();

GregorianCalendar calendar = new GregorianCalendar();
calendar.set(1856, 7, 9);
Inventor inventor = new Inventor("Alice Miller", calendar.getTime(), "America");

ieee.addMember(inventor);

List<Inventor> list = (List<Inventor>) parser.parseExpression("Members.?[Name == 'Alice Miller']").getValue(simpleEvaluationContext, ieee);
System.out.println(list.get(0).getName());//Alice Miller

6.17 Collection projection - 集合映射

Spring表达式也支持映射,(与context类型无关)。例如:

ExpressionParser parser = new SpelExpressionParser();
EvaluationContext simpleEvaluationContext = SimpleEvaluationContext.forReadWriteDataBinding().build();
Society ieee = new Society();

GregorianCalendar calendar = new GregorianCalendar();
calendar.set(1856, 7, 9);
Inventor inventor = new Inventor("Alice Miller", calendar.getTime(), "America");
Inventor inventor1 = new Inventor("Bob White", calendar.getTime(), "America");

ieee.addMember(inventor);
ieee.addMember(inventor1);

List<String> list = (List<String>) parser.parseExpression("Members.![Name]").getValue(simpleEvaluationContext, ieee);
System.out.println(list);//[Alice Miller, Bob White]

6.18 Templated expression - 模板表达式

表达式模板允许文字文本与一个或多个解析块的混合。 你可以每个解析块分隔前缀和后缀的字符, 当然,常见的选择是使用#{}作为分隔符。SimpleEvaluationContext也支持,例如:

ExpressionParser parser = new SpelExpressionParser();
EvaluationContext simpleEvaluationContext = SimpleEvaluationContext.forReadWriteDataBinding().build();
String str = parser.parseExpression(
        "The result of '1 == 2' is #{1 == 2}",
        new TemplateParserContext()).getValue(String.class);

System.out.println(str);//The result of '1 == 2' is false

本文使用的例子:https://github.com/hjkyoyo/spring-expression-study/
参考文章:
https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#expressions-evaluation