表达式引擎Aviator基本介绍及使用以及基于Aviator的规则引擎(附代码详细介绍)

2023-10-30

1. Aviator

1.1 什么是Aviator?

Aviator是一门高性能、轻量级的Java语言实现的表达式求值引擎,主要用于表达式的动态求值。

1.2 为什么需要Aviator?

Aviator的设计目标是 轻量级 和 高性能 ,相对于Groovy、JRuby的笨重,Aviator非常小,加上依赖包就450K,不算依赖包就70K,不过Aviator的语法受限,它并不是一门完整的语言,只是语言的一小部分集合。

1.3 Aviator的特点

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

1.4 Aviator的功能

• 支持大部分运算操作符,包括算数运算符、关系运算符、逻辑操作符、正则匹配操作符、三元表达式,并且还致辞操作符的优先级以及括号的强制优先级。
• 支持函数调用和自定义函数
• 支持正则表达式匹配
• 自动类型转换,当执行操作的时候,会自动判断操作数类型并做相应的转换,无法转换就抛异常
• 支持传入变量,支持类似a.b.c的嵌套变量访问。
• 性能优秀

1.5 使用场景

• 规则判断以及规则引擎
• 公式计算
• 动态脚本控制
等等…

1.6 怎么使用Aviator?

AviatorScript使用文档:AviatorScript语法文档
创建空的Maven项目,导入如下的依赖

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

一个简单的例子:

//Aviator的使用都是集中通过com.googlecode.aviator.AviatorEvaluator这个入口类来处理
public void test1() {
    Long sum = (Long)AviatorEvaluator.execute("1 + 2 + 3");
    System.out.println(sum);
}
//结果为: 6

注意点: Aviator的数值类型只支持Long和Double,任何整数都将会转换成Long,任何浮点数都会转换成Double,包括用户传入的变量数值。
其它例子:

public class AviatorSimpleTest {
    /**
     * 算数表达式
     */
    @Test
    public void test1() {
        Long sum = (Long)AviatorEvaluator.execute("1 + 2 + 3");
        System.out.println(sum);
    }
    /**
     * 逻辑表达式
     */
    @Test
    public void test2() {
        Boolean result = (Boolean)AviatorEvaluator.execute("3 > 1 && 2 != 4 || true");
        System.out.println(result);
    }
    /**
     * 往表达式传入值
     */
    @Test
    public void test3() {
        Map<String, Object> env = new HashMap<>();
        env.put("name", "ruilin.shao");
        String str = "'hello ' + name";
        String result = (String) AviatorEvaluator.execute(str, env);
        System.out.println(result);
        //写法二
        String result2 = (String)AviatorEvaluator.exec(str, "便利蜂");
        System.out.println(result2);
    }
    /**
     * 三元表达式
     */
    @Test
    public void test4() {
        String result = (String)AviatorEvaluator.execute("3 > 0 ? yes : no");
        System.out.println(result);
    }
    /**
     * 函数调用
     */
    @Test
    public void test5() {
        System.out.println("string.length('hello') = " + AviatorEvaluator.execute("string.length('hello')"));//求字符串长度,不能用String.length();
        System.out.println("string.contains('hello', 'h') = " + AviatorEvaluator.execute("string.contains('hello', 'h')"));//判断字符串中是否包含某个字符串
        System.out.println("math.pow(-3, 2) = " + AviatorEvaluator.execute("math.pow(-3, 2)"));
        System.out.println("math.sqrt(9.0) = " + AviatorEvaluator.execute("math.sqrt(9.0)"));
    }
}

1.7 Aviator自定义函数

Aviator除了自己内部定义了一些函数外,还支持自定义函数,只需要实现com.googlecode.aviator.runtime.type.AviatorFunction接口,并注册到AviatorEvaluator即可使用
创建自定义函数:

public class UserDefinedFunction extends AbstractFunction {
    //自定义功能的名字
    @Override
    public String getName() {
        return "multi";
    }
    @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());
    }
}

使用自定义函数

@Test
public void test6() {
    //注册函数
    AviatorEvaluator.addFunction(new UserDefinedFunction());
    System.out.println("multi(2, 3) = " + AviatorEvaluator.execute("multi(2, 3)"));
    //移除函数
    AviatorEvaluator.removeFunction("multi");
    //移除后再执行如下语句会报错
    //System.out.println("multi(2, 3) = " + AviatorEvaluator.execute("multi(2, 3)"));
}

1.8 编译表达式

其实每次执行Aviator.execute()时,背后都经过了编译和执行的操作。
那么我们可以先编译表达式,返回一个编译后的结果,然后传入不同的env来重复使用编译的结果,这样可以提高性能。

/**
 * 编译表达式和未编译表达式性能测试
 */
@Test
public void test8() {
    String expression = "a * (b + c)";
    Map<String, Object> env = new HashMap<>();
    env.put("a", 3.32);
    env.put("b", 234);
    env.put("c", 324.2);
    //编译表达式
    Expression compliedExp = AviatorEvaluator.compile(expression);
    long startTime = System.currentTimeMillis();
    for (int i = 0; i < 10000; i++) {
        Double result = (Double) compliedExp.execute(env);
    }
    long endTime = System.currentTimeMillis();
    System.out.println("预编译的耗时为:" + (endTime - startTime));
    long startTime2 = System.currentTimeMillis();
    for (int i = 0; i < 10000; i++) {
        Double result = (Double) AviatorEvaluator.execute(expression, env);
    }
    long endTime2 = System.currentTimeMillis();
    System.out.println("无编译的耗时为:" + (endTime2 - startTime2));
}

通过complie方法可以将表达式编译成Expression的中间对象,当要执行表达式的时候传入Map对象直接调用Expression的execute方法即可。
编译后的结果可以自己缓存,也可以交给Aviator来帮你缓存,AviatorEvaluator内部又一个全局的缓存池,如果想通过Aviator来帮你缓存,可以通过如下方法:

public static Expression complie(String expression, boolean cached);

举例:

/**
 * 预编译缓存举例
 */
@Test
public void test9() {
    String expression1 = "a + b + c";
    Expression compiledExp1 = AviatorEvaluator.compile(expression1, true);
    Expression compiledExp2 = AviatorEvaluator.compile(expression1, true);
    Expression compiledExp3 = AviatorEvaluator.compile(expression1, false);
    System.out.println("compiledExp1 == compiledExp2 : " + (compiledExp1 == compiledExp2));
    System.out.println("compiledExp1 == compiledExp3 : " + (compiledExp1 == compiledExp3));
}
//compiledExp1 == compiledExp2 : true
//compiledExp1 == compiledExp3 : false

2. 规则引擎

2.1 为什么需要规则引擎?

没有规则引擎的情况下,有些逻辑复杂的业务代码,只能不断的去增添if - else来满足复杂的业务场景,当if - else过多时,会导致代码极难阅读,虽然能通过策略模式来优化if - else,但是依旧无法解决开发缓慢、需要上线的问题。
在风控系统中,风控的逻辑在不断的发生一个变化,如果在代码中写死,逻辑一发生变化,我们就需要改一下代码,再上线一次,在频繁变更逻辑的情况下,这种做法显然无法接收,所以我们需要规则引擎来改变这个现状,通过高效可靠的方式去做这些业务规则的改变。

2.2 规则引擎的好处

• 降低开发成本
• 业务人员独立配置业务规则,开发人员无需理解,以往需要业务人员告诉开发人员,开发人员需要理解才能开发,并且还需要大量的测试来确定是否正确,而且开发结果还容易和提出的业务有偏差,种种都导致开发成本上升
• 增加业务的透明度,业务人员配置之后其它人业务人员也能看到,
• 提高了规则改动的效率和上线的速度

2.3 基于Aviator的规则引擎

通过规则引擎,我们只需要将业务人员配置的规则转换成一个规则字符串,然后将该规则字符串保存进数据库中,当使用该规则时,只传递该规则所需要的参数,便可以直接计算出结果,我们开发人员无需再为这些规则编写任何代码。

public class AviatorExampleTwo {
    //规则可以保存在数据库中,mysql或者redis等等
    Map<Integer, String> ruleMap = new HashMap<>();
    public AviatorExampleTwo() {
        //秒数计算公式
        ruleMap.put(1, "hour * 3600 + minute * 60 + second");
        //正方体体积计算公式
        ruleMap.put(2, "height * width * length");
        //判断一个人是不是资深顾客
        ruleMap.put(3, "age >= 18 && sumConsume > 2000 && vip");
        //资深顾客要求修改
        ruleMap.put(4, "age > 10 && sumConsume >= 8000 && vip && avgYearConsume >= 1000");
        //判断一个人的年龄是不是大于等于18岁
        ruleMap.put(5, "age  >= 18 ? 'yes' : 'no'");
    }
    public static void main(String[] args) {
        AviatorExampleTwo aviatorExample = new AviatorExampleTwo();
        //选择规则,传入规则所需要的参数
        System.out.println("公式1:" + aviatorExample.getResult(1, 1, 1, 1));
        System.out.println("公式2:" + aviatorExample.getResult(2, 3, 3, 3));
        System.out.println("公式3:" + aviatorExample.getResult(3, 20, 3000, false));
        System.out.println("公式4:" + aviatorExample.getResult(4, 23, 8000, true, 2000));
        System.out.println("公式5:" + aviatorExample.getResult(5, 12));
    }
    public Object getResult(int ruleId, Object... args) {
        String rule = ruleMap.get(ruleId);
        return AviatorEvaluator.exec(rule, args);
    }
}
//公式1:3661
//公式2:27
//公式3:false
//公式4:true
//公式5:no
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

表达式引擎Aviator基本介绍及使用以及基于Aviator的规则引擎(附代码详细介绍) 的相关文章

  • Spring Batch如何作为Reader读取多个表(查询)并将其写入平面文件写入

    在我的项目中 我读取了具有不同查询的多个表 并将这些结果集合并到平面文件中 我该如何实现这一目标 我的意思是 JdbcReader 直接采用 1 个选择查询 我如何自定义它 如果 JdbcCursorItemReader 不能满足您的需求
  • 在 Java 中重置 Graphics2D 对象

    我正在用 Java 尝试 Graphics2D 但像往常一样 我被困住了 P 问题是 假设我有这个代码 Graphics2D g Graphics2D this getGraphics Inside a JFrame g rotate Ma
  • Windows 上的虚假唤醒。是否可以?

    我最近学习了 虚假唤醒 有人说这个问题只可能发生在某些类型的 Linux PC 上 我用的是窗户 我为虚假唤醒编写了测试 我得到的结果是这是可能的 但我想向您展示这个测试 也许我在某个地方犯了错误 我的初始变体 import java ut
  • 如何从 .t​​xt 文件读取数据并将数据放入对象的数组列表中?

    到目前为止 我所写的内容是基于我目前对基本数组的了解 但我只是不明白如何使用数组列表 或如何从文件中读取 到目前为止我所写的内容有效 任何有助于修复我的代码以从文件中读取并使用数组列表的链接或建议将不胜感激 谢谢 public class
  • java中如何围绕另一个移动对象旋转一个对象?

    我对 Java 很陌生 想要编写一个简单的太阳系统 其中月球绕地球旋转 地球绕太阳旋转 一切正常 除了月亮不想正确移动 由于地球偏离月球的初始位置 月球的自转半径会根据该距离而增大 同样 当地球接近月球惯性位置时 自转半径会减小 如果初始位
  • 使用 ScheduledExecutorService 安排每月任务

    我想在该月的某一天的特定时间安排一项任务 每次运行之间的间隔可以设置在 1 到 12 个月之间 在java中 可以使用ScheduledExecutorService以固定的时间间隔调度任务 既然一个月的天数不固定 那么如何实现呢 提前致谢
  • JTable AutoCreateRowSorter 将数字排序为字符串

    我有一个 JTable JTable table new JTable String colNames c1 DefaultTableModel model new DefaultTableModel Integer x new Integ
  • 仅使用 ServletContext 查找应用程序的 URL

    我正在使用 Spring MVC 编写一个 Java Web 应用程序 我有一个后台进程 它会遍历数据库并查找必须通过电子邮件发送给我的用户的通知 这些电子邮件需要包含应用程序的超链接 对于网络应用程序来说 这似乎是相当常见的模式 但我遇到
  • 如何在Spring Security SAML示例中配置IDP元数据和SP元数据?

    我想处理 Spring Security SAML 为此 我开始探索Spring安全SAML http docs spring io spring security saml docs 1 0 x reference html chapte
  • Java 创建 Thread 实例时会发生什么

    我有一个关于 Java 线程和操作系统线程的问题 我读了Java 线程与 Pthreads https stackoverflow com questions 5269535 java threads vs pthreads and Jav
  • Spring Batch:比较数据库之间的数据

    我有两个数据库 Oracle 和 MySQL 目标是将Oracle表中的值保存到MySQL中 要求 MySQL表中不存在数据 但我在理解 Spring Batch 时遇到了困难 步骤中 它包含itemReader itemProcessor
  • 序言中不允许引用

    请帮我找到这个异常的原因 我使用以下罐子 core renderer jar itext paulo 155 jar 第一个文档 xhtml lt xml version 1 0 encoding UTF 8 gt lt DOCTYPE h
  • Java 中 LINQ 的等价物是什么? [关闭]

    Closed 此问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 Java 中 LINQ 的等价物是什么 没有什么比 LINQ for Java 更好的了 Edit 现在
  • JFreeChart MeterPlot

    我目前正在用java做Agent项目 在某些时候 我需要显示一个仪表 例如 电池电量 我的程序中有 5 个代理 每个代理都会创建自己的带有名称的仪表图 但不知何故他们没有更新数据集 或者他们正在更新数据集 只是它没有显示在仪表图上 任何想法
  • 无法运行简单的 doclet 程序:包 com.sun.javadoc 不存在

    我正在尝试运行一个简单的 doclet 程序 但无法编译它 javac cp cygdrive c Progra 2 Java jdk1 8 0 65 lib tools jar A java 但它抛出 A java 1 错误 包 com
  • 当相应的 JTextfield 为空时,如何填充 JTable 中的所有项目

    我正在 Java 项目中设计一个高级搜索选项sqlite在 NetBeans 中 有5种不同JTextfields和 5 列 我想填充JTable具有相应的匹配标准 如果一个JTextfield为空 那么它应该选择该列的所有项目 我使用的查
  • 在调试模式下,哪些代码更改会自动反映在 Eclipse 中?

    我使用 eclipse 用于编写 调试 作为 IDE 在调试模式下 当我进行一些更改 例如初始化局部变量 时 它们会自动反映 但其他更改例如更改静态变量的值 有时我会收到一条消息 说我需要重新启动虚拟机 有时则不需要 现在的问题是哪些类型的
  • 仅在java中使用数组计算50的阶乘

    我是java的初学者 我有一个作业要编写一个完整的程序 使用数组计算 50 的阶乘 我无法使用像 biginteger 这样的任何方法 我只能使用数组 因为我的教授希望我们理解背后的逻辑 我猜 然而 他并没有真正教我们数组的细节 所以我在这
  • SAXParseException:找不到元素“定义”的声明

    我对 camunda 和 DMN 完全陌生 我试图在 spring boot 中运行 DMN 示例 链接在这里 https github com camunda camunda bpm examples tree master dmn en
  • 跳过一行GridBagLayout

    我在 JFrame 上使用 GridBagLayout 我希望能够跳过一两行 但将这些行显示为空白 然后在这些行后面有一个按钮 我在文档中找不到任何方法来执行我所描述的操作 有谁知道我可以执行此操作的任何方法吗 发现它比添加空组件干净得多

随机推荐