起源

Aviator 起源于 2010 年左右,当时我还在淘宝中间件,读了龙书,了解了 ASM ,开始读 clojure 编译器的源码。

刚好同时在做 Notify 这个内部消息中间件的一个重要版本实现,其中有一个需求是实现 AMQP 规范里的 header 订阅,允许用户指定一个布尔表达式来决定是否订阅某种类型的消息,需要对布尔表达式字符串进行求值。

当时正处于造轮子的兴奋期,因此就写了第一个版本出来,第一个版本花了一两天的功夫,整个代码结构其实到现在也没有特别大的改变,还是那么乱 :)

表达式引擎当时国内开源的已经有 IKExpression,可惜是纯解释执行的,效率很一般,Groovy 刚开始流行,性能不错,但是整体很重量级,更重要的原因是我们希望控制用户能使用的语法和函数,需要一个定制的“子集”,因此 Aviator 就诞生了。

后来这个库用到了很多公司的很多地方,并且不少场景都是出乎我的意料之外的,常见的公式计算不提,也有用在数据处理转换、数据核对以及工作流逻辑判定、鉴权校验等等。这也是我继续发展它的动力。

原理和特点

Aviator 的基本过程是将表达式直接翻译成对应的 java 字节码执行,整个过程最多扫两趟(开启执行优先模式,如果是编译优先模式下就一趟),这样就保证了它的性能超越绝大部分解释性的表达式引擎,测试也证明如此;其次,除了依赖 commons-beanutils 这个库之外(用于做反射)不依赖任何第三方库,因此整体非常轻量级,整个 jar 包大小哪怕发展到现在 5.0 这个大版本,也才 430K。

同时, Aviator 内置的函数库非常“节制”,除了必须的字符串处理、数学函数和集合处理之外,类似文件 IO、网络等等你都是没法使用的,这样能保证运行期的安全,如果你需要这些高阶能力,可以通过开放的自定义函数来接入。

因此总结它的特点是:

● 高性能 ● 轻量级 ● 一些比较有特色的特点: ○ 支持运算符重载 ○ 原生支持大整数和 BigDecimal 类型及运算,并且通过运算符重载和一般数字类型保持一致的运算方式。 ○ 原生支持正则表达式类型及匹配运算符 =~ ○ 类 clojure 的 seq 库及 lambda 支持,可以灵活地处理各种集合 ● 开放能力:包括自定义函数接入以及各种定制选项

AviatorScript:5.0

任何使用 5.x 版本的朋友,请升级到 5.2.6 及以上版本。

Aviator 原来的定位一直只是一个表达式引擎,不支持 if/else 条件语句(仅有三元运算符支持 ?: ),没有内置的 for/while 循环支持(虽然你可以用 seq 库类似函数式的方式来处理集合),也没有赋值(后来在 4.0 引入),没有作用域的概念(也在 4.0 引入 lambda 函数后部分实现)等等一般语言常见的能力。

不过在 5.0 这个大版本(也就是本指南介绍的主要版本)将支持上述所有的能力,变成一门通用的脚本语言 AviatorScript:

● 词法作用域 {…} ,和 let 定义作用域内的变量 ● return 语句,用于从函数或者 script 中返回(值)。 ● if/elsif/else 条件语句 ● for/while 循环语句,以及 break / continue 支持 ● fn 语法用于定义命名函数, 4.0 已经引入了 lambda -> … end 语法专门用于匿名函数定义 ● ## 单行注释 支持 ● 和 Java Scripting API 更好的集成 ● 字符串插值 ● 异常处理 try…catch…finally 语句等等。

没有改变的是:

● 继续保持轻量化。 ● 还是 two pass 编译,最终生成 JVM 字节码,保证性能比一般解释型脚本快。 ● 内置函数继续“节制”,但是现在也有很灵活的方式来调用任何 Java 类的方法。 ● 几乎向前兼容,不兼容的地方很少,我们将在发布版本里说明。

为什么还要有 aviator ?

这是有人向我提出来的问题,既然 groovy/kotlin/jruby 都发展的这么好,还有没有必要发展 AviatorScript?我直接用他们不也是挺好的。

我的答案是没错,优先使用社区广泛使用的语言,有一个比较好的社区支持,这都是很好、很正确的考量。

那么为什么还想要发展和去使用 AviatorScript?

我能想到的理由如下:

● 我可以,并且我将继续维护它 ● 你不想使用一个全功能的、相对重量级的语言,你只是做一些布尔表达式判定、数据集合处理等等,你不想引入一堆依赖,并且期待有一定的性能保证。AviatorScript 提供了大量的定制选项,甚至各种语法特性都是可以开关的。 ● 你的表达式或者 script 是用户输入的,你无法保证他们的安全性,你希望控制用户能使用的 API,提供一个相对安全的运行沙箱 ● 你原来的项目在使用 aviator,有一堆遗留的表达式要维护,原来没有很好的条件语句、循环语句等能力支持,让你的使用受限了。 ● 你就是喜欢 AviatorScript 的语法 ● 你就是想用它

参考资料

https://www.yuque.com/boyan-avfmj/aviatorscript/guhmrc