Aviator-表达式求值引擎

简介

Aviator 是一个高性能、轻量级的java语言实现的表达式求值引擎,主要用于各种表达式的动态求值。现在已经有很多开源可用的java表达式求值引擎,为什么还需要Avaitor呢?

Aviator的设计目标是轻量级高性能 ,相比于Groovy、JRuby的笨重,Aviator非常小,加上依赖包也才450K,不算依赖包的话只有70K;当然,Aviator的语法是受限的,它不是一门完整的语言,而只是语言的一小部分集合。

其次,Aviator的实现思路与其他轻量级的求值器很不相同,其他求值器一般都是通过解释的方式运行,而Aviator则是直接将表达式编译成Java字节码,交给JVM去执行。简单来说,Aviator的定位是介于Groovy这样的重量级脚本语言和IKExpression这样的轻量级表达式引擎之间。

github地址: https://github.com/killme2008/aviatorscript

推荐看完本文后再到官方文档学习使用:

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

使用

依赖

<dependency>
  <groupId>com.googlecode.aviator</groupId>
  <artifactId>aviator</artifactId>
  <version>{version}</version>
</dependency>

执行表达式

Aviator的使用都是集中通过com.googlecode.aviator.AviatorEvaluator这个入口类来处理,最简单的例子,执行一个计算1+2+3的表达式:

import com.googlecode.aviator.AviatorEvaluator;
 public class SimpleExample {
     public static void main(String[] args) {
         Long result = (Long) AviatorEvaluator.execute("1+2+3");
         System.out.println(result); //6
     }
 }

AviatorEvaluator.execute()可以用直接执行表达式。

细心的朋友肯定注意到结果是Long,而不是Integer。这是因为Aviator的数值类型仅支持Long和Double,任何整数都将转换成Long,任何浮点数都将转换为Double,包括用户传入的变量数值。这个例子的打印结果将是正确答案 6。

使用变量

Expression compiledExp = AviatorEvaluator.compile("max(57,a,b)");
HashMap<String, Object> map = new HashMap<>();
map.put("a", 100);map.put("b", 99);
final Object execute = compiledExp.execute(map);
System.out.println(execute);//100

AviatorEvaluator.compile 编译好表达式,表达式含有未知变量,你可以对一个表达式反复求值。

max() 是自带的函数,用去求 n 个参数中的最大值。

//Expression compiledExp = AviatorEvaluator.compile("你好,我的名称是name");//无法识别表达式报错
//Expression compiledExp = AviatorEvaluator.compile("你好,我的名称是" + "name");//无法识别表达式报错
//Expression compiledExp = AviatorEvaluator.compile("'你好,我的名称是'+ name +'语言'"); 你好,我的名称是java语言
//Expression compiledExp = AviatorEvaluator.compile("'你好,我的名称是'+ name ");//变量前后可以加空格
//Expression compiledExp = AviatorEvaluator.compile("'你好,我的名称是'+name语言");//你好,我的名称是null
Expression compiledExp = AviatorEvaluator.compile("'你好,我的名称是'+name");//你好,我的名称是java
HashMap<String, Object> map = new HashMap<>();
map.put("name", "java");
System.out.println(compiledExp.execute(map));

System.out.println(AviatorEvaluator.execute(" 'a\\\\"b'"));//a"b
System.out.println(AviatorEvaluator.execute(" 'a\\\\\\\\'b'"));//a'b
System.out.println(AviatorEvaluator.execute(" 'hello '+3"));//hello 3
System.out.println(AviatorEvaluator.execute(" 'hello '+ you"));//hello null

上面的例子演示了怎么向表达式传入变量值,表达式中的name是一个变量,默认为null,通过传入Map<String,Object>的变量绑定环境,将name设置为你输入的名称。env的key是变量名,value是变量的值。字符串变量通过 **+变量名**来标记,中间可以加空格。

Aviator的String是任何用单引号或者双引号括起来的字符序列,String可以比较大小(基于unicode顺序),可以参与正则匹配,可以与任何对象相加,任何对象与String相加结果为String。String中也可以有转义字符,如/n、//、/'等。

自定义函数


import com.googlecode.aviator.runtime.function.AbstractFunction;
import com.googlecode.aviator.runtime.function.FunctionUtils;
import com.googlecode.aviator.runtime.type.AviatorBigInt;
import com.googlecode.aviator.runtime.type.AviatorObject;

import java.util.Map;

/**
 * ClassName: CustomerIfFunction
 * Description: 自定义function
 *
 * @author systemcaller
 * @date 2022/5/25 18:43
 **/
public class CustomerIfFunction extends AbstractFunction {

    /**
     * 计算表达式 CUSTOMER.IF(arg1,arg2,agr3)时
     * arg1为真返回arg2,否则返回arg3
     *
     * @param env
     * @param arg1 第一个参数
     * @param arg2 第二个参数
     * @param arg3 第三个参数
     * @return
     */
    @Override
    public AviatorObject call(Map<String, Object> env, AviatorObject arg1, AviatorObject arg2, AviatorObject arg3) {
        return FunctionUtils.getBooleanValue(arg1, env)
                ? new AviatorBigInt(FunctionUtils.getNumberValue(arg2, env))
                : new AviatorBigInt(FunctionUtils.getNumberValue(arg3, env));
    }

    @Override
    public String getName() {
        return "CUSTOMER.IF";
    }
}

public static void main(String[] args) {
     //注册函数
     AviatorEvaluator.addFunction(new IFFunction());
     final Map<String, Object> parmas = Map.of("this", 10000);
     System.out.println(AviatorEvaluator.execute("CUSTOMER.IF(this>1000,1,0)",parmas));   //1
}

注册函数通过AviatorEvaluator.addFunction方法,移除可以通过removeFunction。可以添加任意个自定义函数,函数之间可以任意组合。

注意事项

我在时候时遇到过程中遇到了内存占用过大问题,因为我的业务要进行大量的表达式计算,通过查阅官网文档,发现可以采用 编译缓存模式,降低内存占用。

SystemCaller
SystemCaller

https://gravatar.com/noisily745e35dad0

文章: 47

留下评论

您的邮箱地址不会被公开。 必填项已用 * 标注