Updated at 2020/04/22: 更新到 5.0 版本,增加 FEATURE_SET、MAX_LOOP_COUNT 说明

Options支持一些功能的自定义选项,详解如下。

优化级别:OPTIMIZE_LEVEL

优化级别,更精确地说是运行的优化倾向,可以是:

  • AviatorEvaluator.EVAL,默认值,以运行时的性能优先,编译会花费更多时间做优化,目前会做一些常量折叠、公共变量提取的优化。适合长期运行的表达式。
  • AviatorEvaluator.COMPILE,以编译的性能优先,不会做任何编译优化,牺牲一定的运行性能,适合需要频繁编译表达式的场景。

计算精度:MATH_CONTEXT

Decimal 数字类型的运算精度,默认是 java.util.MathContext.DECIMAL128

解析浮点数:ALWAYS_PARSE_FLOATING_POINT_NUMBER_INTO_DECIMAL

是否将所有浮点数解析为 Decimal 类型,适合需要高精度运算的场景,并且不想为每个浮点数字指定 M 后缀(表示 Decimal 类型)。默认为 false 不开启。

跟踪运行:TRACE_EVAL

是否跟踪运行,打开后将在控制台打印整个表达式的求值过程。请勿在生产环境打开,将极大地降低性能。默认为 false 关闭。

正则分组捕获:PUT_CAPTURING_GROUPS_INTO_ENV

表示正则匹配的时候,是否将捕获的分组放入 env 环境中,例如

  1. email=~/([\\w0-8]+)@\\w+[\\.\\w+]+/ ? $1:'unknow'

将 email 变量中的用户名部分(@ 符号之前)匹配出来,并放到 $1变量中,如果关闭 PUT_CAPTURING_GROUPS_INTO_ENV(设置为 false),将不会将捕获的分组放入 env,也就无法获取到匹配的分组。默认为 true 开启。

变量语法糖:ENABLE_PROPERTY_SYNTAX_SUGAR

是否启用变量访问的语法糖,默认情况下 Aviator 会通过 commons-beantuils 反射访问类似 a.b.c 这样的嵌套 JavaBean 变量,或者 #list.[0].name 这样的数组(链表)中的元素。但是部分用户可能想关闭这个行为,强制都从 env 中获取这些变量值,那么就可以将该选项关闭,也就是设置为 false。默认为 true 开启。

变量处理:NIL_WHEN_PROPERTY_NOT_FOUND

在启用变量访问糖的情况下,如果反射调用失败,默认的行为将抛出运行时异常,而不是返回 null。可以通过本选项改变这个行为,启用的情况下(也就是设置为 true),将不抛出异常,而是返回 null。 默认为 false 关闭。

Env 处理:USE_USER_ENV_AS_TOP_ENV_DIRECTLY

从 4.0 开始,为了支持 lambda, aviator 引入了变量作用域 scope 的概念,本来的默认行为是不再修改用户传入的 env 对象,但是后面看到比较多的用户依赖这个行为,因此提供了这个新选项 USE_USER_ENV_AS_TOP_ENV_DIRECTLY,当为 true 的时候就会将用户传入的 env 作为最顶层的作用域 scope 来使用,并且默认为 true 启用。如果你不需要 aviator 产生副作用污染你传入的 env,这个选项更推荐设置为 false

浮点数解析:ALWAYS_PARSE_INTEGRAL_NUMBER_INTO_DECIMAL

是否将整型数字都解析为 BigDecimal,默认为 false,也就是不启用。在所有数字都是需要高精度计算的场景,结合 ALWAYS_PARSE_FLOATING_POINT_NUMBER_INTO_DECIMAL 选项,可以减少一些类型转换。

参数捕获:CAPTURE_FUNCTION_ARGS

是否捕获函数调用点的参数列表,如果启用,那么类似 func 这样的自定义函数:

  1. func(a, b, c, 100+2)

这样的表达式,就可以在 func 内得到调用的参数列表,

  1. @Override
  2. public AviatorObject call(final Map<String, Object> env, final AviatorObject arg1,
  3. final AviatorObject arg2, final AviatorObject arg3, final AviatorObject arg4) {
  4. List<FunctionArgument> args = FunctionUtils.getFunctionArguments(env);
  5. ......
  6. }

得到的 args 就是参数列表:

  1. [FunctionArgument, index=0, expression="a"]
  2. [FunctionArgument, index=1, expression="b"]
  3. [FunctionArgument, index=2, expression="c"]
  4. [FunctionArgument, index=3, expression="100+2"]

你可以直接从 env 中通过 __args__ 变量直接获取:

  1. List<FunctionArgument> args = (List<FunctionArgument>)env.get("__args__");

在 lambda 表达式中也可以捕获参数列表并使用,捕获的调用参数列表存放在 __args__ 变量:

  1. List<FunctionArgument> args = (List<FunctionArgument>) AviatorEvaluator
  2. .execute("f = lambda(a,bc, d) -> __args__ end; f(1,2,100+2)");
  3. assertEquals(3, args.size());
  4. System.out.println(args);
  5. assertEquals(0, args.get(0).getIndex());
  6. assertEquals("1", args.get(0).getExpression());
  7. assertEquals(1, args.get(1).getIndex());
  8. assertEquals("2", args.get(1).getExpression());
  9. assertEquals(2, args.get(2).getIndex());
  10. assertEquals("100+2", args.get(2).getExpression());

利用这一特性,你可以在运行时对调用的参数做参数校验或者分支派发,以及对于函数调用过程做优化,例如对针对参数做结果缓存等等。不过这一特性会对性能带来稍许损失。

循环次数控制:MAX_LOOP_COUNT

5.0 新增选项。

限制循环语句的最大次数,这个循环包括 for 语句、 while 循环语句以及 map, filter, some 等任何涉及 sequence 遍历的高阶函数。用于限制用户传入的脚本执行循环的次数,避免死循环或者耗费大量 CPU 的场景出现。

默认值: 0,表示无限制。
可以设置为任意正整数,比如 5000,表示单次循环最大次数是 5000。

语法特性:FEATURE_SET

5.0 新增选项。
**
设置 AviatorScript 支持的语法特性集合,它接受的是一个 Set<Feature> 的集合,Feature 包括:

  • Assignment 赋值
  • Return 返回语句
  • If 条件语句
  • ForLoop for 循环语句,包括 break/continue。
  • WhileLoop while 循环语句,包括 break/continue
  • Let 局部变量定义 let 语句
  • LexicalScope 大括号定义词法作用域
  • Lambda 匿名函数 lambda 定义
  • Fn 命名函数的 fn 定义
  • InternalVars 内部变量,如 __instance____env__ 等。
  • Module 模块系统,包括 exportsrequire/load 函数
  • ExceptionHandle 异常处理,包括 try/catch/finally/throw 语句
  • NewInstance 创建对象的 new 语法支持
  • StringInterpolation 字符串插值
  • Use 是否启用 use 语法导入 java 类到当前上下文,方便 new 或者 catch 异常类等。
  • StaticFields 是否启用静态字段直接访问,类似 Long.MAX_VALUE 等。
  • StaticMethods 是否启用静态方法直接使用(基于反射),类似 Math.abs(d) 等。

你可以自定义语法特性集合:

  1. package com.googlecode.aviator.example;
  2. import com.googlecode.aviator.AviatorEvaluator;
  3. import com.googlecode.aviator.AviatorEvaluatorInstance;
  4. import com.googlecode.aviator.Feature;
  5. import com.googlecode.aviator.Options;
  6. /**
  7. * Configure engine example
  8. *
  9. * @author dennis(killme2008@gmail.com)
  10. *
  11. */
  12. public class ConfigureInstanceExample {
  13. public static void main(final String[] args) {
  14. AviatorEvaluatorInstance instance = AviatorEvaluator.newInstance();
  15. instance.setOption(Options.USE_USER_ENV_AS_TOP_ENV_DIRECTLY, false);
  16. instance.setOption(Options.FEATURE_SET,
  17. Feature.asSet(Feature.Assignment,
  18. Feature.ForLoop,
  19. Feature.WhileLoop,
  20. Feature.Lambda,
  21. Feature.Let));
  22. System.out.println(instance
  23. .execute("let square = lambda(x) -> x*2 end; for x in range(0, 10) { p(square(x)); }"));
  24. }
  25. }

这里我们启用了赋值、循环、let 以及 lambda 语法支持,然后执行一个简单脚本。 Feature.asSet 方法方便地生成一个语法特性集合。

如果我们没有启用某个语法特性,执行引擎将报错,比如假设我们将上面的 Feature.Let 移除:

  1. Exception in thread "main" com.googlecode.aviator.exception.UnsupportedFeatureException: Feature.Let is not enabled
  2. at com.googlecode.aviator.parser.ExpressionParser.ensureFeatureEnabled(ExpressionParser.java:138)
  3. at com.googlecode.aviator.parser.ExpressionParser.statement(ExpressionParser.java:1364)
  4. at com.googlecode.aviator.parser.ExpressionParser.statements(ExpressionParser.java:1493)
  5. at com.googlecode.aviator.parser.ExpressionParser.parse(ExpressionParser.java:920)
  6. at com.googlecode.aviator.AviatorEvaluatorInstance.innerCompile(AviatorEvaluatorInstance.java:1293)
  7. at com.googlecode.aviator.AviatorEvaluatorInstance.compile(AviatorEvaluatorInstance.java:1256)
  8. at com.googlecode.aviator.AviatorEvaluatorInstance.compile(AviatorEvaluatorInstance.java:1207)
  9. at com.googlecode.aviator.AviatorEvaluatorInstance.execute(AviatorEvaluatorInstance.java:1383)
  10. at com.googlecode.aviator.AviatorEvaluatorInstance.execute(AviatorEvaluatorInstance.java:1400)
  11. at com.googlecode.aviator.AviatorEvaluatorInstance.execute(AviatorEvaluatorInstance.java:1436)
  12. at com.googlecode.aviator.example.ConfigureInstanceExample.main(ConfigureInstanceExample.java:24)

这个选项的默认值是 Feature.getFullFeatures() ,也就是启用所有的语法特性,如果你想使用 5.0 之前的兼容模式,只启用赋值、lambda 以及内部变量,可以用 Feature.getCompatibleFeatures()

禁用和启用单个选项可以通过 AviatorEvaluatorInstanceenableFeature(feature)disableFeature(feature) 方法。

类的白名单:ALLOWED_CLASS_SET

设置在 new 语句和静态方法(变量)调用中允许使用的 class 白名单集合:

  1. final HashSet<Object> classes = new HashSet<>();
  2. classes.add(ArrayBlockingQueue.class);
  3. this.instance.setOption(Options.ALLOWED_CLASS_SET, classes);

请注意:

  • null 表示不限制(默认值)
  • 空集合表示禁止任何 class