Aviator简介
Aviator 的基本过程是将表达式直接翻译成对应的 java 字节码执行,整个过程最多扫两趟(开启执行优先模式,如果是编译优先模式下就一趟),这样就保证了它的性能超越绝大部分解释性的表达式引擎,测试也证明如此;其次,除了依赖 commons-beanutils 这个库之外(用于做反射)不依赖任何第三方库,因此整体非常轻量级,整个 jar 包大小哪怕发展到现在 5.0 这个大版本,也才 430K。同时, Aviator 内置的函数库非常“节制”,除了必须的字符串处理、数学函数和集合处理之外,类似文件 IO、网络等等你都是没法使用的,这样能保证运行期的安全,如果你需要这些高阶能力,可以通过开放的自定义函数来接入。
因此总结它的特点是:
-
支持运算符重载
-
原生支持大整数和 BigDecimal 类型及运算,并且通过运算符重载和一般数字类型保持一致的运算方式。
-
原生支持正则表达式类型及匹配运算符 =~
-
类 clojure 的 seq 库及 lambda 支持,可以灵活地处理各种集合
官方文档
:https://www.yuque.com/boyan-avfmj/aviatorscript/guhmrc
Aviator的特性
-
支持大部分运算操作符,包括算术操作符、关系运算符、逻辑操作符、正则匹配操作符(=~)、三元表达式?:,并且支持操作符的优先级和括号强制优先级,具体请看后面的操作符列表。
-
支持函数调用和自定义函数。
-
支持正则表达式匹配,类似Ruby、Perl的匹配语法,并且支持类Ruby的$digit指向匹配分组。
-
自动类型转换,当执行操作的时候,会自动判断操作数类型并做相应转换,无法转换即抛异常。
-
支持传入变量,支持类似a.b.c的嵌套变量访问。
-
性能优秀。
Aviator的限制
-
没有if else、do while等语句,没有赋值语句,没有位运算符;
-
仅支持逻辑表达式、算术表达式、三元表达式和正则匹配。
最新jar包
<dependency>
<groupId>com.googlecode.aviator</groupId>
<artifactId>aviator</artifactId>
<version>5.3.0</version>
</dependency>
Aviator编译和执行
AviatorScript 引擎
AviatorScript 编译和执行的入口是AviatorEvaluatorInstance类,该类的一个实例就是一个编译和执行的单元,这个单元我们称为一个 AviatorScript 引擎,你可以多个引擎,每个引擎可以设置不同的编译和运行选项。
编译脚本文件
将表达式编译成JVM字节码,最终的结果对象为Expression,所有的表达式最终编译的结果都是一个Expression对象,他经过:
-
Lexer文法分析;
-
Parser语法解析;
-
第一趟优化:常量折叠、常量池等简单优化;
-
第二趟生成JVM字节码,并最终动态生成一个匿名的Class;
-
实例化Class得到最终的Expression。
每次调用 compileScript(path) 都生成一个新的匿名类和对象,因此如果频繁调用会占满 JVM 的 metaspace,可能导致 full gc 或者 OOM(关于这一点我们将在最佳实践里更详细的解释),因此还有一个方法 compileScript(path, cached) 可以通过第二个布尔值参数决定是否缓存该编译结果。
执行
编译产生的 Expression 对象,最终都是调用 execute() 方法执行,得到结果。但是 execute 方法还可以接受一个变量列表组成的 map,使得表达式更加灵活。
使用示例
@Test
public void testExpressCompile(){
String expression = "if(a > b){p('a大于b');}else{p('a小于或等于b');return a+b;} ";
Map<String,Object> env = new HashMap<>(16);
env.put("a", 100);
env.put("b", 1000);
AviatorEvaluatorInstance instance = AviatorEvaluator.getInstance();
Object execute = instance.execute(expression, env);
System.out.println(execute);
* 打印结果如下:
* a小于或等于b
* 1100