当前位置: 首页 > 工具软件 > Aviator > 使用案例 >

表达式引擎Aviator

吴建中
2023-12-01


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?

创建空的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
 类似资料: