aviator表达式引擎学习

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

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

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

Aviator 的特性

  • 支持绝大多数运算操作符,包括算术操作符、关系运算符、逻辑操作符、位运算2.符、正则匹配操作符(=~)、三元表达式(?:)
  • 支持操作符优先级和括号强制设定优先级
  • 逻辑运算符支持短路运算。
  • 支持丰富类型,例如nil、整数和浮点数、字符串、正则表达式、日期、变量等,支持自动类型转换。
  • 内置一套强大的常用函数库
  • 可自定义函数,易于扩展
  • 可重载操作符
  • 支持大数运算(BigInteger)和高精度运算(BigDecimal)
  • 性能优秀

maven依赖

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

使用

AviatorEvaluator是一个全局静态变量

      Object obj = AviatorEvaluator.execute("123 + 4654");
      System.out.println(obj);

结果

4777

Aviator的数值类型仅支持Long和Double, 任何整数都将转换成Long, 任何浮点数都将转换为Double, 包括传入的变量数值

         //整数计算
        Object obj = AviatorEvaluator.execute("123 + 4654");
        System.out.println("123 + 4654 结果的类型:"+obj.getClass());

        //小数计算
        Object obj1 = AviatorEvaluator.execute("0.05+0.01");
        System.out.println("0.05+0.01 结果的类型:"+obj1.getClass());

结果

123 + 4654 结果的类型:class java.lang.Long
0.05+0.01 结果的类型:class java.lang.Double

精度计算

开启 ALWAYS_PARSE_FLOATING_POINT_NUMBER_INTO_DECIMAL 选项,那么在表达式中出现的浮点数都将解析为 BigDecimal

       //未开启精度计算
        Object obj1 = AviatorEvaluator.execute("0.05+0.01");
        System.out.println("0.05+0.01 = " + obj1);
        //精度计算
        AviatorEvaluator.setOption(Options.ALWAYS_PARSE_FLOATING_POINT_NUMBER_INTO_DECIMAL, true);
        Object obj2 = AviatorEvaluator.execute("0.05+0.01");
        System.out.println("0.05+0.01 = " + obj2);

结果

0.05+0.01 = 0.060000000000000005
0.05+0.01 = 0.06

使用变量

        Map<String,Object> map1 = new HashMap<>();
        map1.put("field01","you ");
        map1.put("field02","are ");
        map1.put("field03","cool");
        Object obj1 = AviatorEvaluator.execute("field01 + field02 + field03",map1);
        System.out.println(obj1);

        Map<String,Object> map2 = new HashMap<>();
        map2.put("field01",100);
        map2.put("field02",65);
        map2.put("field03",35);
        Object obj2 = AviatorEvaluator.execute("field01 + field02 + field03",map2);
        System.out.println(obj2);

结果

you are cool
200

自定义函数

Aviator 除了内置的函数之外,还允许用户自定义函数,只要实现com.googlecode.aviator.runtime.type.AviatorFunction接口, 并注册到AviatorEvaluator即可使用.

  • AviatorFunction接口十分庞大, 通常来说你并不需要实现所有的方法, 只要根据你的方法的参 数个数, 继承AbstractFunction类并override相应方法即


    image.png
public class AviService {
    public static void main(String[] args) {
        Map<String,Object> map2 = new HashMap<>();
        map2.put("field01",100);
        map2.put("field02",65);
        AviatorEvaluator.addFunction(new AddFunction());
        Object obj2 = AviatorEvaluator.execute("add(field01,field02)",map2);
        System.out.println(obj2);

    }
}
class AddFunction 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 AviatorDouble(left.doubleValue() + right.doubleValue());
    }    
    public String getName() {
        return "add";
    }
}
  • 参数个数不确定,可以继承 AbstractVariadicFunction 类,只要实现其中的 variadicCall 方法即可
public class AviService {
    public static void main(String[] args) {

        Map<String, Object> map2 = new HashMap<>();
        map2.put("field01", 100);
        map2.put("field02", 65);
        map2.put("field03", 35);
        AviatorEvaluator.addFunction(new AddFunction());
        Object obj2 = AviatorEvaluator.execute("add(field01,field02,field03)", map2);
        System.out.println(obj2);
    }
}

class AddFunction extends AbstractVariadicFunction {

    public AviatorDouble variadicCall(Map<String, Object> env, AviatorObject... args) {
        double i = 0;
        for (AviatorObject arg : args) {
            Object value = arg.getValue(env);
            double v = Double.parseDouble(value.toString());
            i = i + v;
        }
        return new AviatorDouble(i);
    }

    @Override
    public String getName() {
        return "add";
    }
}

使用Java类方法作为自定义函数

从 4.2.2 开始, aviator 还提供了一个更便捷地批量将某个类的静态方法导入为自定义函数的方式

public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException {

        Map<String, Object> map2 = new HashMap<>();
        map2.put("field01", "HELLO_WORD");
        AviatorEvaluator.addStaticFunctions("ArStringUtil", ArStringUtil.class);
        Object obj2 = AviatorEvaluator.execute("ArStringUtil.camelName(field01)", map2);
        System.out.println(obj2);
        
        map2.put("field02", 12345678);
        Object obj3 = AviatorEvaluator.execute("ArStringUtil.number2Chinese(field02)", map2);
        System.out.println(obj3);
    }

结果

helloWord
一千二百三十四万五千六百七十八
  • 支持导入实例方法
  • 调用可变参数方法
  • 批量导入方法和注解支持
  • 重载运算符

编译表达式

    public static void main(String[] args){

        String rule = "field01 + field02";
        Map<String, Object> map2 = new HashMap<>();
        map2.put("field01", 1000);
        map2.put("field02", 2000);

        Expression compiledExp = AviatorEvaluator.compile(rule, true);
        Object obj = compiledExp.execute(map2);
        System.out.println(obj);

    }

编译后的结果你可以自己缓存, 也可以交给 Aviator 帮你缓存, AviatorEvaluator内部有一个全局的缓存池, 如果你决定缓存编译结果, 可以通过:

public static Expression compile(java.lang.String expression, boolean cached) 

三元运算符

        map2.put("field01", 100);
        String rule = "file01 > 200 ? '大于200' : '小于200'";
...
        System.out.println(obj);//小于200

正则表达式

        String rule = "field01 =~ /^[A-Z]+$/";
        map2.put("field01", "ABC");
...
        System.out.println(obj);//true

变量的语法糖

        Map<String, Object> user = new HashMap<>();
        user.put("name","张三");
        user.put("age",18);
        List<String> son = new ArrayList<>();
        son.add("小明");
        son.add("小方");
        user.put("son",son);
        map2.put("user",user);
        String rule = "user.name + '的年龄是' + user.age + ',他的儿子是' + user.son[0] + '和' + user.son[1]";
        //张三的年龄是18,他的儿子是小明和小方

nil 对象

nil是 Aviator 内置的常量,类似 java 中的null,表示空的值。nil跟null不同的在于,在 java 中null只能使用在==、!=的比较运算符,而nil还可以使用>、>=、<、<=等比较运算符。 Aviator 规定,任何对象都比nil大除了nil本身。用户传入的变量如果为null,将自动以nil替代。

    AviatorEvaluator.execute("nil == nil");   //true
    AviatorEvaluator.execute(" 3> nil");      //true
    AviatorEvaluator.execute(" true!= nil");  //true
    AviatorEvaluator.execute(" ' '>nil ");    //true
    AviatorEvaluator.execute(" a==nil ");     //true, a 是 null

日期比较

Aviator 并不支持日期类型,如果要比较日期,你需要将日期写字符串的形式,并且要求是形如 “yyyy-MM-dd HH:mm:ss:SS”的字符串,否则都将报错。 字符串跟java.util.Date比较的时候将自动转换为Date对象进行比较:

        Date date = new SimpleDateFormat("yyyy-MM-dd").parse("2019-10-10");
        Date date1 = new SimpleDateFormat("yyyy-MM-dd").parse("2019-10-10");
        env.put("date", date);
        env.put("date1", date1);
        
        Object obj = AviatorEvaluator.execute("date == date1 ", env);
        System.out.println(obj);  // true

数据类型

  • String 字符串类型,单引号或者双引号括起来的文本串
  • 布尔值: 常量true和false
  • 正则表达式, 以//括起来的字符串
  • Number类型: 数字类型,支持四种类型,分别是long,double,java.math.BigInteger(简称 big int)和java.math.BigDecimal(简 称 decimal),规则如下:
    任何以大写字母 N 结尾的整数都被认为是 big int
    任何以大写字母 M 结尾的数字都被认为是 decimal
    其他的任何整数都将被转换为 Long
    其他任何浮点数都将被转换为 Double
    超过 long 范围的整数字面量都将自动转换为 big int 类型

操作符

算术运算符

Aviator 支持常见的算术运算符,包括+ - * / %五个二元运算符,和一元运算符-(负)。其中- * / %和一元的-仅能作用于Number类型。
+不仅能用于Number类型,还可以用于String的相加,或者字符串与其他对象的相加。
Aviator 规定,任何类型与String相加,结果为String。

逻辑运算符

Avaitor 的支持的逻辑运算符包括,一元否定运算符!,以及逻辑与的&&,逻辑或的||。逻辑运算符的操作数只能为Boolean
&&||都执行短路规则。

关系运算符

Aviator 支持的关系运算符包括<, <=, >, >=以及==!=
关系运算符可以作用于Number之间、String之间、Pattern之间、Boolean之间、变量之间以及其他类型与nil之间的关系比较, 不同类型除了nil之外不能相互比较。

位运算符

Aviator 支持所有的 Java 位运算符,包括&, |, ^, ~, >>, <<, >>>

匹配运算符

匹配运算符=~用于StringPattern的匹配,它的左操作数必须为String,右操作数必须为Pattern。 匹配成功后,Pattern的分组将存于变量$num,num为分组索引。

三元运算符

Aviator 没有提供if else语句,但是提供了三元运算符?:,形式为bool ? exp1: exp2。 其中bool必须为Boolean类型的表达式, 而exp1exp2可以为任何合法的 Aviator 表达式,并且不要求exp1exp2返回的结果类型一致。

内置函数

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容