这款轻量级规则引擎,真香!

2023年 8月 9日 39.7k 0

大家好,我是老三,之前同事用了一款轻量级的规则引擎脚本AviatorScript,老三也跟着用了起来,真的挺香,能少写很多代码。这期就给大家介绍一下这款规则引擎。

简介

AviatorScript 是一门高性能、轻量级寄宿于 JVM (包括 Android 平台)之上的脚本语言。

它起源于2010年,作者对当时已有的一些产品不是很满意,所以自己撸了一个,它是Groovy的一个定制化的子集。

在这里插入图片描述

相比较一些传统的规则引擎,比如DroolsJessJRules,它更加轻量级,而且性能更好,同时能力开放,扩展很方便。

我们来看(吹)看(吹)AviatorScript的特点:

  • 它支持数字、字符串、正则表达式、布尔值等基本类型,并且可以使用所有 Java 运算符进行运算。
  • 还有一个内置的东西叫做 bigintdecimal,可以处理超大整数和高精度运算。而且我们还可以通过运算符重载让它们使用普通的算术运算符 +-*/
  • 语法非常齐全,可以用它来写多行数据、条件语句、循环语句,还能处理词法作用域和异常处理等等。
  • 如果我们喜欢函数式编程,还有一个叫做 Sequence 抽象的东西,可以让你更方便地处理集合。
  • 还有一个轻量化的模块系统,方便我们组织代码。
  • 如果我们需要调用 Java 方法,也没问题,可以用多种方式方便地调用 Java 方法,还有一个完整的脚本 API可以让你从 Java 调用脚本。
  • 性能也是超出想象的好,如果使用 ASM 模式,它会直接将脚本翻译成 JVM 字节码,解释模式还可以在 Android 等非标准 Java 平台上运行。
  • AviatorScript可以用在各种场景,比如规则判断和规则引擎、公式计算、动态脚本控制,甚至集合数据 ELT 等等。可以说相当全能了。

    快速开始

    AviatorScript 是一门寄生在 JVM (Hosted on the JVM)上的语言,类似 clojure/scala/kotlin 等等,我们从写个Hello World开始。

    • 创建一个SpringBoot项目,引入依赖,这里选择的是最新版本
            
                com.googlecode.aviator
                aviator
                5.3.3
            
    

    PS:可以看到aviator的groupId有一个googlecode,但是它和Google可没什么关系,这是因为早期aviator托管在Google的一个开源项目托管平台Google Code。

    • 在项目的resource目录下创建一个目录script,在script目录下创建脚本hello.av
    println("Hello, AviatorScript!");
    
    • 编写一个单元测试,运行脚本
        @Test
        void testHello() throws Exception {
            //获取路径
            ClassPathResource resource = new ClassPathResource("script/hello.av");
            String scriptPath = resource.getPath();
            //编译
            Expression exp = AviatorEvaluator.getInstance().compileScript(scriptPath);
            //执行
            exp.execute();
        }
    

    最后执行一下,就可以看到输出:

    Hello, AviatorScript!
    
    • 我们也可以直接把脚本定义成字符串,用compile()来进行编译
        @Test
        void testHelloStr() throws Exception {
            //定义脚本
            String script="println("Hello, AviatorScript!");";
            //编译
            Expression exp = AviatorEvaluator.getInstance().compile(script);
            //执行
            exp.execute();
        }
    

    AviatorScript有一个Idea插件,支持直接编译运行Aviator脚本,比较方便。

    Aviator插件

    但不足之处,这个插件已经不怎么维护了,只兼容到了Idea2021版本。

    Idea插件

    AviatorScript脚本的运行,分为两步,编译执行

    编译执行

    编译支持编译脚本文件和脚本文本,分别使用compileScriptcompile方法。

    编译产生的 Expression 对象,最终都是调用 execute() 方法执行。

    这里有个重要能力,execute 方法可以接受一个变量列表组成的 map,来注入执行的上下文:

            String expression = "a-(b-c) > 100";
            Expression compiledExp = AviatorEvaluator.compile(expression);
            //上下文
            double a=100.3,b=45,c= -199.100;
            Map context=new HashMap();
            context.put("a",a);
            context.put("b",b);
            context.put("c",c);
            //通过注入的上下文执行
            Boolean result = (Boolean) compiledExp.execute(context);
            System.out.println(result);
    

    我们实现一些规则的判断就是基于这个能力,把一些参数下上下文传进去,然后进行逻辑判断。

    基本语法

    我们在来看看AviatorScript的基本语法,它的语法相当简洁,比较接近于数学表达式的形式。

    基本类型及运算

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

    数字

    AviatorScript 支持数字类型,包括整数和浮点数,以及高精度计算(BigDecimal)。数字类型可以进行各种算术运算。

    整数和算术运算

    整数类型,对应Java中的long类型,可以表示范围为 -9223372036854774808 ~ 9223372036854774807 的整数。整数可以使用十进制或十六进制表示。

    let a = 99;
    let b = 0xFF;
    let c = -99;
    
    println(a + b); // 270
    println(a / b); // 0
    println(a - b + c); // -156
    println(a + b * c); // -9801
    println(a - (b - c)); // 198
    println(a / b * b + a % b); // 99
    

    整数可以进行加减乘除和取模运算。需要注意的是,整数相除的结果仍然是整数,遵循整数运算规则。可以使用括号来指定运算的优先级。

    浮点数

    浮点数类型对应Java中的double类型,表示双精度 64 位浮点数。浮点数可以使用十进制或科学计数法表示。

    let a = 1.34159265;
    let b = 0.33333;
    let c = 1e-2;
    
    println(a + b); // 1.67492265
    println(a - b); // 1.00826265
    println(a * b); // 0.4471865500145
    println(a / b); // 4.0257402772554
    println(a + c); // 1.35159265
    
    

    浮点数可以进行加减乘除运算,结果仍然为浮点数。

    高精度计算(Decimal)

    高精度计算使用 BigDecimal 类型,可以进行精确的数值计算,适用于货币运算或者物理公式运算的场景。可以通过在数字后面添加 "M" 后缀来表示 BigDecimal 类型。

    let a = 1.34M;
    let b = 0.333M;
    let c = 2e-3M;
    
    println(a + b); // 1.673M
    println(a - b); // 1.007M
    println(a * b); // 0.44622M
    println(a / b); // 4.022022022M
    println(a + c); // 1.342M
    

    BigDecimal 类型可以进行加减乘除运算,结果仍然为 BigDecimal 类型。默认的运算精度是 MathContext.DECIMAL128,可以通过修改引擎配置项 Options.MATH_CONTEXT 来改变。

    数字类型转换

    数字类型在运算时会自动进行类型转换:

    • 单一类型参与的运算,结果仍然为该类型。
    • 多种类型参与的运算,按照 long -> bigint -> decimal -> double 的顺序自动提升,结果为提升后的类型。

    可以使用 long(x) 函数将数字强制转换为 long 类型,使用 double(x) 函数将数字强制转换为 double 类型。

    let a = 1;
    let b = 2;
    
    println("a/b is " + a/b); // 0
    println("a/double(b) is " + a/double(b)); // 0.5
    

    a 和 b 都是 long 类型,它们相除的结果仍然是整数。使用 double(b) 将 b 转换为 double 类型后,相除的结果为浮点数。

    字符串

    字符串类型由单引号或双引号括起来的连续字符组成。可以使用 println 函数来打印字符串。

    let a = "hello world";
    println(a); // hello world
    

    字符串的长度可以通过 string.length 函数获取。

    let a = "hello world";
    println(string.length(a)); // 11
    

    字符串可以通过 + 运算符进行拼接。

    let a = "hello world";
    let b = "AviatorScript";
    println(a + ", " + b + "!" + 5); // hello world, AviatorScript!5
    
    

    字符串还包括其他函数,如截取字符串 substring,都在 string 这个 namespace 下,具体见函数库列表。

    布尔类型和逻辑运算

    布尔类型用于表示真和假,它只有两个值 truefalse 分别表示真值和假值。

    比较运算如大于、小于可以产生布尔值:

    println("3 > 1 is " + (3 > 1));  // 3 > 1 is true
    println("3 >= 1 is " + (3 >= 1)); // 3 >= 1 is true
    println("3 >= 3 is " + (3 >= 3)); // 3 >= 3 is true
    println("3 < 1 is " + (3 < 1)); // 3 < 1 is false
    println("3

    相关文章

    JavaScript2024新功能:Object.groupBy、正则表达式v标志
    PHP trim 函数对多字节字符的使用和限制
    新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
    使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
    为React 19做准备:WordPress 6.6用户指南
    如何删除WordPress中的所有评论

    发布评论