4.0 是一个大版本变更,本文将纤细介绍一些主要的功能特性。

求值器实例

4.0 之前, aviator 的求值器是一个全局共享的对象 AviatorEvaluator,使用起来很方便,但是无法在同一个进程内提供不同配置选项或者自定义函数的求值器,因此,从 4.0 开始,aviator 引入了 AviatorEvaluatorInstance,表示一个单独的求值器实例,原有的 AviatorEvaluator 只是默认提供的一个全局单例。

AviatorEvaluatorInstance 提供了原有 AviatorEvaluatorexecutecompile 等方法,如果你有需要定义多个求值器的场景可以使用它:

  1. //创建一个求值器实例,应当尽量复用该实例。
  2. AviatorEvaluatorInstance instance = AviatorEvaluator.newInstance();
  3. //设置自定义选项
  4. instance.setOption(Options.TRACE_EVAL, true);
  5. //正常使用
  6. instance.execute("3+4");
  7. Expression exp = instance.compile("1+3");

多行表达式

允许通过分号 ; 来求值多个表达式,多个表达式的求值结果是最后一个表达式的值

  1. assertEquals(20.0, AviatorEvaluator.execute("a=3; b=2; c=a+b; c*4.0"));

表达式也可以换行,更清楚一些,但是分割符还是分号

  1. a=3;
  2. b=2;
  3. c=a+b;
  4. c*4.0

最后一个表达式可以不加分号。

上面的例子中使用了引入的赋值功能,参见下节。

赋值

从 4.1.0 开始,aviator 引入了变量赋值的能力,你可以给变量赋予任何一个有效的 aviator 类型:

  1. assertEquals(3, AviatorEvaluator.execute("a=1; a+2"));
  2. assertEquals(1, AviatorEvaluator.execute("a=5;b=4.2 ; c= a > b? 1: 0; c"));
  3. assertEquals(1, AviatorEvaluator.execute("a=5;b=4.2 ; c= a > b? 1: 0; c"));

赋值的语法很简单,就是 {var}={val} 的形式。

Lambda 匿名函数

4.0 版本最重大的功能除了赋值之外就是 lambda 匿名函数的引入,你可以直接使用新的 lambda 语法定义函数并使用:

  1. assertEquals(6, AviatorEvaluator.execute("square = lambda(x) -> x *2 end; square(3)"));

上面的例子使用 lambda 语法定义了一个 sqaure 函数:

  1. square = lambda(x) -> x *2 end

然后调用了这个函数,传入参数 3,结果返回 square(3) 的值,也就是 2*3=6

有了 lambda 后,原来对于 seq 库依赖谓词函数来实现的能力都可以用 lambda 实现,例如:

  1. map(a, lambda(x) -> x + 1 end)

为集合 a 的每个元素加上 1。 或者过滤出偶数:

  1. filter(a, lambda(x) -> x%2 == 0 end)

或者拼接两个 list:

  1. reduce(list2, lambda(list1, x) -> seq.add(list1, x) end, list1)

所有高阶函数使用到函数的地方都可以用 lambda 替代。

除此之外, lambda 还支持闭包捕获,可以从上下文中捕获变量使用:

  1. AviatorEvaluator.defineFunction("test",
  2. "lambda(x) -> lambda(y) -> lambda(z) -> x + y + z end end end");
  3. AviatorEvaluator.execute("test(4)(5)(6)", env); // => 输出 15

这里使用了新的自定义函数的方式 AviatorEvaluator.defineFunction(name, lambda) ,定义了一个 test 函数,这个函数是一个三层嵌套的 lambda 函数:

  1. lambda(x) ->
  2. lambda(y) ->
  3. lambda(z) ->
  4. x + y + z
  5. end
  6. end
  7. end"

可以看到,这里定义了三个嵌套的匿名函数,前两层匿名函数都是返回一个 lambda 函数作为返回值,在 aviator 4.0.0 里面,函数也是作为 first class 存在,可以作为参数、返回值使用。

那么 test(4)(5)(6) 的求值顺序是

  • test(4) 返回一个匿名函数 f1, f1 捕获了传入的参数 4
  • f1(5) 返回另一个匿名函数 f2, f2 这个函数捕获了传入的参数 5
  • f2(6) 将前两步传入的参数和现在的参数 6 累加 4+5+6 ,得到 15 返回。

这就是所谓闭包(closure),匿名函数块从上下文中捕获变量并保存,在脱离当前上下文后还可以继续使用,有点类似 java 的匿名类。

闭包和赋值

被闭包捕获的变量,不允许后面再被赋值,这跟 java 匿名类使用到变量要求是 final 是一个道理,避免引入一些隐藏的 bug:

  1. String exp = "a = 1; b = lambda(x) -> a+ x end ; a = 4 ; b(5)";
  2. AviatorEvaluator.execute(exp);

上述的执行将报错:

  1. Exception in thread "main" com.googlecode.aviator.exception.ExpressionRuntimeException: Can't assignment value to captured variable.The `a` is already captured by lambda.
  2. at com.googlecode.aviator.utils.Env.put(Env.java:217)
  3. at com.googlecode.aviator.utils.Env.put(Env.java:1)
  4. at com.googlecode.aviator.runtime.type.AviatorJavaType.setValue(AviatorJavaType.java:268)
  5. at Script_1548816270231_0/1269169234.execute0(Unknown Source)
  6. at com.googlecode.aviator.ClassExpression.execute(ClassExpression.java:72)
  7. at com.googlecode.aviator.AviatorEvaluatorInstance.execute(AviatorEvaluatorInstance.java:763)
  8. at com.googlecode.aviator.AviatorEvaluator.execute(AviatorEvaluator.java:439)
  9. at com.googlecode.aviator.AviatorEvaluator.execute(AviatorEvaluator.java:451)

因为 a 已经被闭包 b 捕获,不可以继续赋值为 4。

函数加载器

自定义函数可以通过 AviatorEvaluator 或者 AviatorEvaluatorInstanceaddFunction 添加,也可以通过 classpath 下的 aviator_functions.config 配置文件来添加。

从 4.0.0 开始,新增 com.googlecode.aviator.FunctionLoader 接口,可以自定义实现函数加载器:

  1. public interface FunctionLoader {
  2. /**
  3. * Invoked when function not found
  4. *
  5. * @param name function name
  6. */
  7. public AviatorFunction onFunctionNotFound(String name);
  8. }

当某个函数找不到的时候,将按照顺序从函数加载器中查找,找到即返回。自定义加载器需要注册到求值器对象:

  1. AviatorEvaluator.addFunctionLoader(loader);

取消注册通过 removeFunctionLoader(loader) 方法。

默认提供的是 ClassPathConfigFunctionLoader 加载器,也就是从 classpath 的 aviator_functions.config 文件中加载。此外,还提供了一个从 spring 容器加载的实现,但是默认不注册,可以主动注册:

  1. ApplicationContext ctx = new ClassPathXmlApplicationContext("context.xml");
  2. SpringContextFunctionLoader loader = new SpringContextFunctionLoader(ctx);
  3. AviatorEvaluator.addFunctionLoader(loader);

当函数找不到的时候,将从 spring 容器按照 function name 作为 bean name 来查找自定义函数实现。

新增选项

新增三个选项:

  • Options.ENABLE_PROPERTY_SYNTAX_SUGAR,是否启用属性访问的语法糖,例如 a.b.c 的变量访问是否走 commons-beanutils 反射访问,默认启用为 true。禁用后,这类变量将直接从 env 获取。
  • Options.NIL_WHEN_PROPERTY_NOT_FOUND,当启用属性语法糖的时候,如果调用反射失败,默认行为是抛出异常,通过启用这个选项,将直接返回 null,忽略异常。
  • Options.DISABLE_ASSIGNMENT,是否关闭赋值功能。当设置为 true 的时候,将禁止赋值。默认为 false。
  • Options.USE_USER_ENV_AS_TOP_ENV_DIRECTLY 选项,默认为 true,表示将直接使用用户的 env 作为 top 级别的 env 使用,兼容老版本的行为 #74 #94。从安全角度,如果没有依赖这个行为的,推荐设置为 false。
  • Opitons.TRACE_EVAL 跟踪运行,支持函数调用跟踪,默认不启用,该选项极大影响性能,仅推荐在测试 debug 使用。

其他

  1. AviatorEvaulatorInstanceAviatorEvaulator 新增方法 isExpressionCached()getExpressionCacheSize() 分别用于判断表达式是否缓存编译结果,以及返回缓存的编译表达式总数。
  2. 新增两个内置的特殊变量:
  • #__env__ 表示运行环境的 env 上下文。
  • #__instance__ 表示当前运行的求值器 AviatorEvaluatorInstance实例。
  1. 新增内置函数参见内置函数列表release 说明