【java表达式引擎】四、高性能、轻量级的AviatorScript

一、Aviator 介绍

github:(https://github.com/killme2008/aviatorscript%60)
参考文档1:https://www.yuque.com/boyan-avfmj/aviatorscript
参考博客2:https://blog.csdn.net/ZhangQingmu/article/details/125087255

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

原理和特点

Aviator 的基本过程是将表达式直接翻译成对应的 java 字节码执行,整个过程最多扫两趟(开启执行优先模式,如果是编译优先模式下就一趟),这样就保证了它的性能超越绝大部分解释性的表达式引擎,测试也证明如此;其次,除了依赖 commons-beanutils 这个库之外(用于做反射)不依赖任何第三方库,因此整体非常轻量级,整个 jar 包大小哪怕发展到现在 5.0 这个大版本,也才 430K。同时, Aviator 内置的函数库非常“节制”,除了必须的字符串处理、数学函数和集合处理之外,类似文件 IO、网络等等你都是没法使用的,这样能保证运行期的安全,如果你需要这些高阶能力,可以通过开放的自定义函数来接入。因此总结它的特点是:

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

二、依赖

目前github上的最新版本是5.3.2
大家可以依赖以下版本,也可以下载源码直接在源码中测试

<dependency>
  <groupId>com.googlecode.aviator</groupId>
  <artifactId>aviator</artifactId>
  <version>5.3.2</version>
</dependency>

三、样例使用

1、需求

这次现提出一些需求看是否都可以满足

  • 常规的 加减乘除
    1+1+3+5-6=4
    (9.88+12.24)-5.88/(5.88+6.14)* (9.88+12.24)= 11.299234608985

  • 逻辑判断
    if(3<6,1,0)
    IFERROR((MIN(5/0,3)),0) = 0

2、上手使用

我们主要使用这两个方法就可以满足上述需求

 AviatorEvaluator.compile
AviatorEvaluator.execute

3、加减乘除

   @Test
   public void test1() {
      Expression exp1 = AviatorEvaluator.compile("1+1+3+5-6");
      Expression exp2 = AviatorEvaluator.compile(" (9.88+12.24)-5.88/(5.88+6.14)* (9.88+12.24)");
      Object execute = exp1.execute();
      Object execute2 = exp2.execute();
      assertEquals(4,execute);
      System.out.println( execute);//4
      System.out.println(execute2);//11.299234608985023
   }

4、逻辑判断

执行test2报错了不支持excel格式的逻辑判断

   @Test
   public void test2() {
      Expression exp1 = AviatorEvaluator.compile("if(3<6,1,0)");
      Expression exp2 = AviatorEvaluator.compile("IFERROR((MIN(5/0,3)),0)");
      Object execute = exp1.execute();
      Object execute2 = exp2.execute();
      System.out.println( execute);//
      System.out.println(execute2);//
   }

支持if else 和三母运算

   /**
    * if else
    */
   @Test
   public void test3() {
      Expression exp1 = AviatorEvaluator.compile("if(1<2) { 3 } else { 4}");
      Expression exp2 = AviatorEvaluator.compile("1<2?  3 : 4 ");
      Object execute = exp1.execute();
      System.out.println( execute);//3
      Object execute2 = exp2.execute();
      System.out.println( execute2);//3
   }

max和min

   /**
    * max和min函数
    */
   @Test
   public void test4() {
      Expression exp1 = AviatorEvaluator.compile("min(3,4)");
      Expression exp2 = AviatorEvaluator.compile("max(3,4)");
      Object execute = exp1.execute();
      System.out.println( execute);//3
      Object execute2 = exp2.execute();
      System.out.println( execute2);//4
   }

小编的一些常见的需求基本可以满足,下面就看一下还支持那些吧。

四、其余介绍

1、基本数据类型

AviatorScript 支持常见的类型,如数字、布尔值、字符串等等,同时将大整数、BigDecimal、正则表达式也作为一种基本类型来支持。

加减乘除对应的运算符就是 +,-,*,/ 这都比较好理解,取模运算符就是 % ,规则和语法和 java 是一样的。

  • 整数
    整数例如 -99、0、1、2、100……等等,对应的类型是 java 中的 long 类型。AviatorScript 中并没有 byte/short/int 等类型,统一整数类型都为 long,支持的范围也跟 java 语言一样:-9223372036854774808~9223372036854774807。

  • 浮点数
    仅支持 double 类型
    浮点数和浮点数的算术运算结果为浮点数,浮点数和整数的运算结果仍然为浮点数。

  • 高精度计算(Decimal)
    浮点数是无法用于需要精确运算的场景,比如货币运算或者物理公式运算等,这种情况下如果在 Java 里一般推荐使用 BigDecimal 类型,调用它的 add/sub 等方法来做算术运算。
    AviatorScript 将 BigDecimal 作为基本类型来支持(下文简称 decimal 类型),只要浮点数以 M 结尾就会识别类型为 deicmal,例如 1.34M 、 0.333M 或者科学计数法 2e-3M 等等。
    如果你觉的为浮点数添加 M 后缀比较麻烦,希望所有浮点数都解析为 decimal ,可以开启 Options.ALWAYS_PARSE_FLOATING_POINT_NUMBER_INTO_DECIMAL

  • 字符串
    在任何语言中,字符串都是最基本的类型,比如 java 里就是 String 类型。AviatorScript 中同样支持字符串,只要以单引号或者双引号括起来的连续字符就是一个完整的字符串对象,例如:

"a"  或者 'a' 

2、逻辑判断

支持if esle elseif 等连续判断

3、循环语句

支持 for 和 while 两种循环语句

五、函数列表

只要是下方的函数,都是可以直接使用的
例如

   /**
    * 使用系统函数
    */
   @Test
   public void test6() {
      Expression exp1 = AviatorEvaluator.compile("min(3,4)");
      Expression exp2 = AviatorEvaluator.compile("rand(100)");
      Object execute = exp1.execute();
      System.out.println( execute);//3
      Object execute2 = exp2.execute();
      System.out.println( execute2);//59
   }

   /**
    * 日期函数
    */
   @Test
   public void test8() {
      Expression exp1 = AviatorEvaluator.compile("date_to_string(date,format)");
      Map<String, Object> env = new HashMap<>();
      env.put("date", new Date());
      env.put("format", "yyyy-MM-dd");
      Object result =  exp1.execute(env);
      System.out.println( result);//2022-09-12


      Expression exp2 = AviatorEvaluator.compile("string_to_date(source,format)");
      Map<String, Object> env2 = new HashMap<>();
      env2.put("source", "2022-01-04");
      env2.put("format", "yyyy-MM-dd");
      Object result2 =  exp2.execute(env2);
      System.out.println( result2);//Tue Jan 04 00:00:00 CST 2022
   }

1、系统函数

函数名称
说明
assert(predicate, [msg])
断言函数,当 predicate 的结果为 false 的时候抛出 AssertFailed 异常, msg 错误信息可选。
sysdate()
返回当前日期对象 java.util.Date
rand()
返回一个介于 [0, 1) 的随机数,结果为 double 类型
rand(n)
返回一个介于 [0, n) 的随机数,结果为 long 类型
cmp(x, y)
比较 x 和 y 大小,返回整数,0 表示相等, 1 表达式 x > y,负数则 x < y。
print([out],obj)
打印对象,如果指定 out 输出流,向 out 打印, 默认输出到标准输出
println([out],obj)
或者
p([out], obj)
与 print 类似,但是在输出后换行
pst([out], e);
等价于 e.printStackTrace(),打印异常堆栈,out 是可选的输出流,默认是标准错误输出
now()
返回 System.currentTimeMillis() 调用值
long(v)
将值转为 long 类型
double(v)
将值转为 double 类型
boolean(v)
将值的类型转为 boolean,除了 nil 和 false,其他都值都将转为布尔值 true。
str(v)
将值转为 string 类型,如果是 nil(或者 java null),会转成字符串 ‘null’
bigint(x)
将值转为 bigint 类型
decimal(x)
将值转为 decimal 类型
identity(v)
返回参数 v 自身,用于跟 seq 库的高阶函数配合使用。
type(x)
返回参数 x 的类型,结果为字符串,如 string, long, double, bigint, decimal, function 等。Java  类则返回完整类名。
is_a(x, class)
当 x 是类 class 的一个实例的时候,返回 true,例如 is_a(“a”, String) ,class 是类名。
is_def(x)
返回变量 x 是否已定义(包括定义为 nil),结果为布尔值
undef(x)
“遗忘”变量  x,如果变量 x 已经定义,将取消定义。
range(start, end, [step])
创建一个范围,start 到 end 之间的整数范围,不包括 end, step 指定递增或者递减步幅。
tuple(x1, x2, …)
创建一个 Object 数组,元素即为传入的参数列表。
eval(script, [bindings], [cached])
对一段脚本文本 script 进行求值,等价于 AviatorEvaluator.execute(script, env, cached)
comparator(pred)
将一个谓词(返回布尔值)转化为 java.util.Comparator 对象,通常用于 sort 函数。
max(x1, x2, x3, …)
取所有参数中的最大值,比较规则遵循逻辑运算符规则。
min(x1, x2, x3, …)
取所有参数中的最小值,比较规则遵循逻辑运算符规则。
constantly(x)
用于生成一个函数,它对任意(个数)参数的调用结果 x。

2、数学函数

math.abs(d)
求 d 的绝对值
math.round(d)
四舍五入
math.floor(d)
向下取整
math.ceil(d)
向上取整
math.sqrt(d)
求 d 的平方根
math.pow(d1,d2)
求 d1 的 d2 次方
math.log(d)
求 d 的自然对数
math.log10(d)
求 d 以 10 为底的对数
math.sin(d)
正弦函数
math.cos(d)
余弦函数
math.tan(d)
正切函数
math.atan(d)
反正切函数
math.acos(d)
反余弦函数
math.asin(d)
反正弦函数

3、字符串函数

date_to_string(date,format)
将 Date 对象转化化特定格式的字符串,2.1.1 新增
string_to_date(source,format)
将特定格式的字符串转化为 Date 对 象,2.1.1 新增
string.contains(s1,s2)
判断 s1 是否包含 s2,返回 Boolean
string.length(s)
求字符串长度,返回 Long
string.startsWith(s1,s2)
s1 是否以 s2 开始,返回 Boolean
string.endsWith(s1,s2)
s1 是否以 s2 结尾,返回 Boolean
string.substring(s,begin[,end])
截取字符串 s,从 begin 到 end,如果忽略 end 的话,将从 begin 到结尾,与 java.util.String.substring 一样。
string.indexOf(s1,s2)
java 中的 s1.indexOf(s2),求 s2 在 s1 中 的起始索引位置,如果不存在为-1
string.split(target,regex,[limit])
Java 里的 String.split 方法一致,2.1.1 新增函数
string.join(seq,seperator)
将集合 seq 里的元素以 seperator 为间隔 连接起来形成字符串,2.1.1 新增函数
string.replace_first(s,regex,replacement)
Java 里的 String.replaceFirst 方法, 2.1.1 新增
string.replace_all(s,regex,replacement)
Java 里的 String.replaceAll 方法 , 2.1.1 新增

4、集合函数

函数名称
说明
repeat(n, x)
返回一个 List,将元素 x 重复 n 次组合而成。
repeatedly(n, f)
返回一个 List,将函数 f 重复调用 n 次的结果组合而成。
seq.array(clazz, e1, e2,e3, …)
创建一个指定 clazz 类型的数组,并添加参数 e1,e2,e3 …到这个数组并返回。 clazz 可以是类似 java.lang.String 的类型,也可以是原生类型,如 int/long/float 等
seq.array_of(clazz, size1, size2, …sizes)
创建 clazz 类型的一维或多维数组,维度大小为 sizes 指定。clazz 同 seq.array 定义。
seq.list(p1, p2, p3, …)
创建一个 java.util.ArrayList 实例,添加参数到这个集合并返回。
seq.set(p1, p2, p3, …)
创建一个 java.util.HashSet 实例,添加参数到这个集合并返回。
seq.map(k1, v1, k2, v2, …)
创建一个 java.util.HashMap 实例,参数要求偶数个,类似 k1,v1 这样成对作为 key-value 存入 map,返回集合。
seq.entry(key, value)
创建 Map.Entry 对象,用于 map, filter 等函数
seq.keys(m)
返回 map 的 key 集合
seq.vals(m)
返回 map 的 value 集合
into(to_seq, from_seq)
用于 sequence 转换,将 from sequence 的元素使用 seq.add 函数逐一添加到了 to sequence 并返回最终的 to_seq
seq.contains_key(map, key)
当 map 中存在 key 的时候(可能为 null),返回 true。对于数组和链表,key 可以是 index,当 index 在有效范围[0…len-1],返回 true,否则返回 false
seq.add(coll, element)
seq.add(m, key, value)
往集合 coll 添加元素,集合可以是 java.util.Collection,也可以是 java.util.Map(三参数版本)
seq.put(coll, key, value)
类似 List.set(i, v)。用于设置 seq 在 key 位置的值为 value,seq 可以是 map ,数组或者 List。 map 就是键值对, 数组或者 List 的时候, key 为索引位置整数,value 即为想要放入该索引位置的值。
seq.remove(coll, element)
从集合或者 hash map 中删除元素或者 key
seq.get(coll, element)
从 list、数组或者 hash-map 获取对应的元素值,对于 list 和数组, element 为元素的索引位置(从 0 开始),对于 hash map 来说, element 为 key。
map(seq,fun)
将函数 fun 作用到集合 seq 每个元素上, 返回新元素组成的集合
filter(seq,predicate)
将谓词 predicate 作用在集合的每个元素 上,返回谓词为 true 的元素组成的集合
count(seq)
返回集合大小,seq 可以是数组,字符串,range ,List 等等
is_empty(seq)
等价于 count(seq) == 0,当集合为空或者 nil,返回 true
distinct(seq)
返回 seq 去重后的结果集合。
is_distinct(seq)
当 seq 没有重复元素的时候,返回 true,否则返回 false
concat(seq1, seq2)
将 seq1 和 seq2 “连接”,返回连接后的结果,复杂度 O(m+n), m 和 n 分别是两个集合的长度。
include(seq,element)
判断 element 是否在集合 seq 中,返回 boolean 值,对于 java.uitl.Set 是 O(1) 时间复杂度,其他为 O(n)
sort(seq, [comparator])
排序集合,仅对数组和 List 有效,返回排序后的新集合,comparator 是一个 java.util.Comparator 实例,可选排序方式。
reverse(seq)
将集合元素逆序,返回新的集合。
reduce(seq,fun,init)
fun 接收两个参数,第一个是集合元素, 第二个是累积的函数,本函数用于将 fun 作用在结果值(初始值为 init 指定)和集合的每个元素上面,返回新的结果值;函数返回最终的结果值
take_while(seq, pred)
遍历集合 seq,对每个元素调用 pred(x),返回 true则加入结果集合,最终返回收集的结果集合。也就是说从集合 seq 收集 pred 调用为 true 的元素。
drop_while(seq, pred)
与 take_while 相反,丢弃任何 pred(x) 为 true 的元素并返回最终的结果集合。
group_by(seq, keyfn)
对集合 seq 的元素按照 keyfn(x) 的调用结果做分类,返回最终映射 map。具体使用见 文档
zipmap(keys, values)
返回一个 HashMap,其中按照 keys 和 values 两个集合的顺序映射键值对。具体使用见 文档
seq.every(seq, fun)
fun 接收集合的每个元素作为唯一参数,返回 true 或 false。当集合里的每个元素调用 fun 后都返回 true 的时候,整个调用结果为 true,否则为 false。
seq.not_any(seq, fun)
fun 接收集合的每个元素作为唯一参数,返回 true 或 false。当集合里的每个元素调用 fun 后都返回 false 的时候,整个调用结果为 true,否则为 false。
seq.some(seq, fun)
fun 接收集合的每个元素作为唯一参数,返回 true 或 false。当集合里的只要有一个元素调用 fun 后返回 true 的时候,整个调用结果立即为该元素,否则为 nil。
seq.eq(value)
返回一个谓词,用来判断传入的参数是否跟 value 相等,用于 filter 函数,如filter(seq,seq.eq(3)) 过滤返回等于3 的元素组成的集合
seq.neq(value)
与 seq.eq 类似,返回判断不等于的谓词
seq.gt(value)
返回判断大于 value 的谓词
seq.ge(value)
返回判断大于等于 value 的谓词
seq.lt(value)
返回判断小于 value 的谓词
seq.le(value)
返回判断小于等于 value 的谓词
seq.nil()
返回判断是否为 nil 的谓词
seq.exists()
返回判断不为 nil 的谓词
seq.and(p1, p2, p3, …)
组合多个谓词函数,返回一个新的谓词函数,当今仅当 p1、p2、p3 …等所有函数都返回 true 的时候,新函数返回 true
seq.or(p1, p2, p3, …)
组合多个谓词函数,返回一个新的谓词函数,当 p1, p2, p3… 其中一个返回 true 的时候,新函数立即返回 true,否则返回 false。
seq.min(coll)
返回集合中的最小元素,要求集合元素可比较(实现 Comprable 接口),比较规则遵循 aviator 规则。
seq.max(coll)
返回集合中的最大元素,要求集合元素可比较(实现 Comprable 接口),比较规则遵循 aviator 规则。

5、自定义函数的使用


   /**
    * 自定义函数minn
    */
   @Test
   public void test7() {
      AviatorEvaluator.addFunction(new MinnFunction());
      String expression = "minn(a,b)";
      Expression compiledExp = AviatorEvaluator.compile(expression);
      Map<String, Object> env = new HashMap<>();
      env.put("a", 5.5);
      env.put("b", 55);
      Double result = (Double) compiledExp.execute(env);
      System.out.println(result);//5.5
   }

   static class MinnFunction extends AbstractFunction {
      @Override public AviatorObject call(Map<String, Object> env, AviatorObject arg1, AviatorObject arg2) {
         Number left = FunctionUtils.getNumberValue(arg1, env);
         Number right = FunctionUtils.getNumberValue(arg2, env);
         return new AviatorBigInt(Math.min(left.doubleValue(), right.doubleValue()));
      }
      public String getName() {
         return "minn";
      }
   }

六、对象中的属性引用

你要访问变量a中的某个属性b, 那么你可以通过a.b访问到, 更进一步, a.b.c将访问变量a的b属性中的c属性值, 推广开来也就是说 Aviator 可以将变量声明为嵌套访问的形式。

  /**
   * 类型转换,及对象中的属性引用
   */
  @Test
  public void testTypeConversation() {
    Map<String, Object> env = new HashMap<String, Object>();
    env.put("foo", new Foo(100));
    env.put("bar", new Bar(99, 999));
    env.put("date", new Date());

    Map<String, Object> map = new HashMap<String, Object>();
    map.put("key", "aviator");
    env.put("tmap", map);
    env.put("bool", Boolean.FALSE);

    // long op long=long
    System.out.println(this.instance.execute("3+3") instanceof Long);
    System.out.println(this.instance.execute("3+3/2") instanceof Long);
    System.out.println(this.instance.execute("foo.a+bar.a", env) instanceof Long);
    System.out.println(this.instance.execute("bar.a+bar.b", env));

    // double op double=double
    System.out.println(this.instance.execute("3.2+3.3") instanceof Double);
    System.out.println(this.instance.execute("3.01+3.1/2.1") instanceof Double);
    System.out.println(this.instance.execute("3.19+3.1/2.9-1.0/(6.0002*7.7+8.9)") instanceof Double);

    // double + long=double
     System.out.println(this.instance.execute("3+0.02") instanceof Double);
     System.out.println(this.instance.execute("3+0.02-100") instanceof Double);
     System.out.println(this.instance.execute("3+3/2-1/(6*7+8.0)") instanceof Double);
     System.out.println(this.instance.execute("foo.a+3.2-1000", env) instanceof Double);

    // object + string =string
    System.out.println( this.instance.execute("'hello '+ 'world'"));
    System.out.println( this.instance.execute("'hello '+tmap.key", env));
    System.out.println(this.instance.execute("true+' '+tmap.key", env));
    System.out.println( this.instance.execute("foo.a+tmap.key", env));
    System.out.println( this.instance.execute("/\\d+/+'hello'"));
    System.out.println( this.instance.execute("3.2+tmap.key", env));
    System.out.println( this.instance.execute("bool+' is false'", env));

}
public class Foo {
    int a;
    public Foo() {
    }
    public Foo(final int a) {
      super();
      this.a = a;
    }
    public int getA() {
      return this.a;
    }
    public void setA(final int a) {
      this.a = a;
    }
  }
  public class Bar extends Foo {
    int b;
    public Bar() {
    }
    public Bar(final int a, final int b) {
      super(a);
      this.b = b;
    }
    public int getB() {
      return this.b;
    }
    public void setB(final int b) {
      this.b = b;
    }

  }
  • 2
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值