任何使用 5.x 版本的朋友,请升级到 5.2.6 及以上版本。
5.0 有哪些特性?
5.0 这个大版本最大的改变是将 aviator 从表达式引擎升级为 AviatorScript 通用脚本语言,主要的语法特性包括:
大括号 { ... }括起来的词法作用域。let语句用于定义局部变量。- 条件语句
if/elsif/else。 - 循环语句
for和while,以及相应的break和continue语句支持。 return语句用于从脚本或者函数中返回值。fn hello() { println("hello"); }新的 fn 语法用于定义命名函数。## 单行注释注释支持- 模块系统
new语法用于创建对象- 异常处理
- 命令行工具 aviator
学习文档可以阅读《AviatorScript 编程指南》,大量例子可以参见 examples 下的脚本。
除了新的语法特性之外,还做了大量的重构和优化,以及新的功能支持,主要包括:
- 编译结果支持 LRU 缓存,通过
AviatorEvaluatorInstance#useLRUExpressionCache(int)即可启用。 - 所有解析 token 和内部对象如 AviatorString 等都实现了
Serializable接口,方便序列化。 - 彻底重构了 Sequence 抽象及各个高阶函数,现在你可以自定义 Sequence。
- 完整支持 Java 脚本 API,包括
Invocable接口等,还提供了 SPI 实现,让 Java 调用 AviatorScript 更容易。 - 优化报错信息,减少不必要的堆栈。
- 函数
AviatorFunction继承Runnable和Callable接口,两个接口实现都将调用无参函数分支。这样函数可以直接作为Runnable或者Callable使用。 - 大量的内部函数重构和优化,包括:
seq.get(seq, x)支持Set,如果存在元素 x,就返回元素自身,否则返回 nilseq.contains_key(seq, index)除了map 之外还支持数组和链表,如果 index 这个索引位置在数组或者链表有效范围内就返回 true,否则返回 false。seq.add、filter、some等函数都支持了所有 sequence 集合,特别是 map,元素类型为Map.Entry。- 现在字符串 String 也是一个 sequence,因此你可以用 map/reduce 等函数以及 for 循环去操作字符串,它的每个元素是单个字符组成的字符串,暂不支持字符类型。
- 新增函数:
type(x)获取值的类型,is_def(x)返回 x 是否被定义,undef(x)取消 x 的定义(或者说遗忘 x)。assert(x)断言函数,当 x 不为 true,抛出异常。cmp(x, y)用于对比 x 和 y,返回整数,当x == y返回 0,x > y返回正整数,否则返回负数。range(start, end, [step])用于创建[start, end)的整数集合。seq.put(seq, key, value)用于设置 seq 在 key 位置的值为 value,seq 可以是 map ,数组或者 List。 map 就是键值对, 数组或者 List 的时候, key 为索引位置整数,value 即为想要放入该索引位置的值。decimal(x)将其他类型的值转为 decimal 类型。bigint(x)将其他类型的值转为 bigint 类型。seq.entry(key, value)用于创建Map.Entry对象。into(to_seq, from_seq)用于 sequence 转换,将 from sequence 的元素使用seq.add函数逐一添加到了 to sequence 并返回最终的 to_seq。
- 性能优化:
include(seq, x)对于Set现在是 O(1) 时间复杂度- 消除 BeanUtils 的全局锁,提升嵌套变量的并发访问和写入性能。
- 通过脚本内符号表,提升变量访问性能
- 优化字符串拼接性能,内部使用 StringBuilder 替代原先的加法运算。
- 其他:
- 新增
AviatorEvaluatorInstance管理 classloader 的 API,如resetClassLoader、setAviatorClassLoader等。 - 编译脚本使用的
compileScript系列方法 - 编译结果缓存允许指定 caching key,减少内存占用,只要保证唯一即可。
FeatureAPI,用于指定脚本引擎支持的语法特性。
- 新增
5.0 的 breaking change 不兼容点有哪些?
已知的不兼容点包括:
- 废弃的:
AviatorRuntimeJavaType(Object)构造函数,请用AviatorRuntimeJavaType.valueOf(Object)替代。 - 移除数个选项:
- 删除
Options.ALWAYS_USE_DOUBLE_AS_DECIMAL,请用Options.ALWAYS_USE_DECIMAL_AS_FLOATING_POINT_NUMBER替代。 - 删除
Options.TRACE选项,不再支持。 - 删除
Options.DISABLE_ASSIGNMENT选项,使用Feature.Assignment来替代用于启用或者禁用。
- 删除
- 默认启用全脚本语法特性支持,包括赋值、
return语句、fn 语法、let、if/else以及for/while等默认都支持,不过可修改,见下一节。 - 变量被闭包捕获后,仍然可修改,这跟原来设定不符。
- 新增关键字:if, elsif, else, for, in, while, break, continue, fn, return, try, catch, finally, throw, new。如果你原来使用了这些关键字做函数或者变量名,可能会遇到问题。
- 部分函数修改了行为,特别是 seq 系列函数:
nil =~/[a-zA-Z]+/; ## returns false when matching nil with regular pattern .count(nil); ## returns 0include(nil, element); ## returns falsemap(nil, lambda); ## returns nilsort(nil); ## returns nilreduce(nil, lambda, init); ## returns the init valuefilter(nil, lambda); ## returns nilseq.every(nil, lambda); ## returns trueseq.not_any(nil, lambda); ## returns trueseq.some(nil, lambda); ## returns nil
如果你发现有新的不兼容点,请在评论里回复,谢谢。
如何无缝升级到 5.0 ?
首先,请仔细阅读上一节的不兼容点,做针对性的修改。
其次,如果你想继续将 AviatorScript 保持在 5.0 之前的 EL 模式,你可以通过下列代码设置语法特性集为兼容模式:
AviatorEvaluator.getInstance().setOption(Options.FEATURE_SET, Feature.getCompatibleFeatures())
我们看下 Feature.getCompatibleFeatures() 返回的是什么:
/*** Returns the feature set that is compatible with aviator early versions(before 5.0).** @return*/public static Set<Feature> getCompatibleFeatures() {return asSet(Feature.Assignment, Feature.Lambda, Feature.InternalVars);}
返回的仅有赋值、lambda 语法和内部变量特性( __env__ 、 __intance__ 等内部变量),其他语法特性都不启用,这就跟 5.0 之前版本保持一致。
如果你想关闭赋值,你可以单独调用 disableFeature 方法:
AviatorEvaluator.getInstance().disableFeature(Feature.Assignment);
启用就是 enableFeature 方法,比如我们想单独启用 return 语句:
AviatorEvaluator.getInstance().enableFeature(Feature.Return);
关于 Opitons.FEATURE_SET 的完整说明,请阅读选项文档。
交流支持请到 github 或者加入钉钉群: 30739878
