Aviator

2022/10/09 Rules Aviator

Aviator

AviatorScript 起源于 2010 年左右,由国内个人开发者Dennis开源的java轻量化规则引擎。能处理运算表达式、逻辑判断表达式、可以使用if/elsif/else 条件语句、可以自定义函数。

使用

Maven依赖

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

第一个示例

## 3.使用说明 Aviator支持两种方式执行表达式, 1)直接使用字符串,把需要执行的表达式用字符串拼接的方法搞定之后直接丢给Aviator,返回值就是运行结果。 2)使用Aviator的脚本文件 .av,语法规则类似于js,可在其中写很多复杂的逻辑判断,调用时候指定文件路径即可。 本人项目使用的是第一种方法,因为规则都是系统上线之后用户自定义配置的,无法提前写好脚本,只能通过字符串拼接的方法执行

使用规则引擎计算表达式

使用Math注解使用规则表达式别名

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD,ElementType.FIELD})
@Documented
public @interface Math {
    /**
     * 计算名称
     * @return
     */
    String name() default "";

}

解析Math注解别名

import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
/**
 * 表达式计算引擎
 */
@Slf4j
public class MathEnv {

    /**
     * 根据对象获取变量和值
     * @param o
     * @return
     */
    public Map<String, Object> getEnv(Object o) {
        Map<String, Object> env = new HashMap<String, Object>(30);
        Field[] fields = o.getClass().getDeclaredFields();
        for (Field f : fields) {
            Math annotation = f.getAnnotation(Math.class);
            if (null != annotation) {
                String name = annotation.name();
                f.setAccessible(true);
                Object fieldVal = null;
                try {
                    fieldVal = f.get(o);
                } catch (IllegalAccessException e) {
                    log.info("exception:{}", e);
                    throw new RuntimeException(e);
                }
                env.put(name, fieldVal);
            }
        }
        return env;
    }
}

表达式引擎执行

import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.Expression;
import lombok.extern.slf4j.Slf4j;

import java.math.BigDecimal;
import java.util.Map;

/**
 * 表达式计算引擎
 */
@Slf4j
public class MathFormula {

    /**
     * 通过对象执行公式
     * @param mathformula
     * @param o
     * @return
     */
    public static BigDecimal executeFormula(String mathformula, Object o) {
        MathEnv mathEnv = new MathEnv();
        Map<String, Object> env = mathEnv.getEnv(o);
        return compileAndExecute(mathformula, env);
    }

    /**
     * 通过Map执行公式
     * @param mathFormula
     * @param env
     * @return
     */
    private static BigDecimal compileAndExecute(String mathFormula, Map<String, Object> env) {
        // 打印计算公式
        log.info("**************************************************");
        log.info("计算公式 => {}", mathFormula);
        Expression compiledExp = AviatorEvaluator.compile(mathFormula);
        // 打印变量和值
        env.entrySet().forEach(e ->log.info("{} => {}", e.getKey(), e.getValue()));
        // 打印结果
        BigDecimal result = (BigDecimal) compiledExp.execute(env);
        log.info("计算结果 => {}", result);
        log.info("**************************************************");
        return result;
    }
}

测试运行


import lombok.Data;
import lombok.ToString;

import java.io.Serializable;
import java.math.BigDecimal;

@Data
@ToString
public class Product implements Serializable {

    @Math(name="收入")
    private BigDecimal in1;

    @Math(name="首付款比率")
    private BigDecimal in2;

    @Math(name="经销商支付首付款银行承兑汇票比率")
    private BigDecimal in3;

    @Math(name="贴息率")
    private BigDecimal in4;

    @Math(name="银承天数")
    private BigDecimal in5;

    @Math(name="首付款比率")
    private BigDecimal in6;

    @Math(name="赊销周期")
    private BigDecimal in7;
}

执行


import java.math.BigDecimal;

public class MathFormulaTest {

    public static void main(String[] args) {
        MathFormulaTest test=new MathFormulaTest();
        System.out.println(test.test());
    }

    public BigDecimal test(){
        // 求服务费收入率
        String s="(收入-首付款比率*经销商支付首付款银行承兑汇票比率*贴息率*银承天数/360)/(1-首付款比率)*(360/赊销周期)";
        Product product=new Product();
        product.setIn1(new BigDecimal("200"));
        product.setIn2(new BigDecimal("100"));
        product.setIn3(new BigDecimal("100"));
        product.setIn4(new BigDecimal("100"));
        product.setIn5(new BigDecimal("100"));
        product.setIn6(new BigDecimal("100"));
        product.setIn7(new BigDecimal("100"));
        BigDecimal bigDecimal = MathFormula.executeFormula(s, product);
        return bigDecimal;
    }


}

先说结论: 规则简单:qlexpress或者avaitor;规则复杂:drools

最终选择是因为:足够轻量级,社区活跃度较好,最新jar包更新于22年4月

官网

git地址

String expression = “a==’河北省’&&((b==’男人’   c>=30)   d==’黄’)”;
// 编译表达式
Expression compiledExp = AviatorEvaluator.compile(expression);

Map<String, Object> env = new HashMap<>();
env.put("a", "河北省");
env.put("b", "男人");
env.put("c",1);
env.put("d","黄");


// 执行表达式
System.out.println( compiledExp.execute(env)); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 1、引入背景 1.1 需求背景及面临问题 系统分案时,需要针对规则进行解析: 示例: ((省份 = 河北省)并且((城市 = 张家口市)或者(城市 = 石家庄市))并且(服务类型 = 信用卡)并且((委托金额 > 1000)并且(委托金额 < 50000)))或者((省份 = 河北省)并且(城市 = 石家庄市)并且(服务类型 = 互联网)) 解析条件: (省份 = 河北省)并且(城市 = 张家口市)并且(服务类型 = 信用卡)并且(委托金额 > 1000)并且(委托金额 < 50000) (省份 = 河北省)并且(城市 = 石家庄市)并且(服务类型 = 信用卡)并且(委托金额 > 1000)并且(委托金额 < 50000) (省份 = 河北省)并且(城市 = 石家庄市)并且(服务类型 = 互联网)

1.2 面临问题 系统架构复杂 系统的可维护性和维护成本提高 系统的整合复杂 增加“硬代码”业务规则的成本和风险

2 为什么选用Aviator 2.1 简介 高性能、轻量级的java语言实现的表达式求值引擎,主要用于各种表达式的动态求值 Aviator的设计目标是轻量级和高性能 ,相比于Groovy、JRuby的笨重,Aviator 非常小,加上依赖包也才450K,不算依赖包的话只有70K 其次,Aviator的实现思路与其他轻量级的求值器很不相同,其他求值器一般都 是通过解释的方式运行,而Aviator则是直接将表达式编译成Java字节码,交给 JVM去执行 简单来说,Aviator的定位是介于Groovy这样的重量级脚本语言和 IKExpression这样的轻量级表达式引擎之间。

2.2 特性介绍 支持大部分运算操作符,包括算术操作符、关系运算符、逻辑操作符、正则匹配操作符 、三元表达式 ,并且支持操作符的优先级和括号强制优先级,具体请看后面的操作符列表。 支持函数调用和自定义函数。 支持正则表达式匹配,类似Ruby、Perl的匹配语法,并且支持类Ruby的$digit指向匹配分组。自动类型转换,当执行操作的时候,会自动判断操作数类型并做相应转换,无法转换即抛异常。 支持传入变量,支持类似a.b.c的嵌套变量访问。 性能优秀。

2.3 整体结构

3 demo 3.1 引入pom 为啥选这个版本,因为这是按时间倒序的第二个稳定版本

com.googlecode.aviator aviator 5.3.0

1 2 3 4 5 6 3.2 执行方式 execute():需要传递Map格式参数 exec():不需要传递Map

public class Test { public static void main(String[] args) { // exec执行方式,无需传递Map格式 String age = “18”; System.out.println(AviatorEvaluator.exec(“‘His age is ‘+ age +’!’”, age));

    // execute执行方式,需传递Map格式
    Map<String, Object> map = new HashMap<String, Object>();
    map.put("age", "18");
    System.out.println(AviatorEvaluator.execute("'His age is '+ age +'!'", map));

} }

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 3.3 支持函数

public class Test { public static void main(String[] args) { Map<String,Object> map = new HashMap<>(); map.put(“s1”,”123qwer”); map.put(“s2”,”123”);

System.out.println(AviatorEvaluator.execute(“string.startsWith(s1,s2)”,map));

} } 1 2 3 4 5 6 7 8 9 10 11 3.4 自定义函数

自定义函数要继承AbstractFunction类,重写目标方法。

public class Test { public static void main(String[] args) { // 注册自定义函数 AviatorEvaluator.addFunction(new MultiplyFunction()); // 方式1 System.out.println(AviatorEvaluator.execute(“multiply(12.23, -2.3)”)); // 方式2 Map<String, Object> params = new HashMap<>(); params.put(“a”, 12.23); params.put(“b”, -2.3); System.out.println(AviatorEvaluator.execute(“multiply(a, b)”, params)); }

}

class MultiplyFunction extends AbstractFunction{ @Override public AviatorObject call(Map<String, Object> env, AviatorObject arg1, AviatorObject arg2) {

    double num1 = FunctionUtils.getNumberValue(arg1, env).doubleValue();
    double num2 = FunctionUtils.getNumberValue(arg2, env).doubleValue();
    return new AviatorDouble(num1 * num2);
}

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

}

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 3.5 运算符优先级

3.6 常量和变量

3.7 表达式Demo

public class Test { public static void main(String[] args) { String expression = “a+(b-c)>100”; // 编译表达式 Expression compiledExp = AviatorEvaluator.compile(expression);

   Map<String, Object> env = new HashMap<>();
   env.put("a", 100.3);
   env.put("b", 45);
   env.put("c", -199.100);

   // 执行表达式
   Boolean result = (Boolean) compiledExp.execute(env);
   System.out.println(result);

} }

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 3.8 数组和集合Demo List和数组用list[0]和array[0],Map用map.date

public class Test { public static void main(String[] args) {

    final List<String> list = new ArrayList<>();
    list.add("hello");
    list.add(" world");

    final int[] array = new int[3];
    array[0] = 0;
    array[1] = 1;
    array[2] = 3;

    final Map<String, Date> map = new HashMap<>();
    map.put("date", new Date());

    Map<String, Object> env = new HashMap<>();
    env.put("list", list);
    env.put("array", array);
    env.put("map", map);

    System.out.println(AviatorEvaluator.execute(
            "list[0]+':'+array[0]+':'+'today is '+map.date", env));

}

}

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 3.9 三元比较符 public class Test { public static void main(String[] args) {

    Map<String, Object> env = new HashMap<String, Object>();
    env.put("a", -5);
    String result = (String) AviatorEvaluator.execute("a>0? 'yes':'no'", env);
    System.out.println(result);
}

} 1 2 3 4 5 6 7 8 9 10 3.10 正则表达式匹配 public class Test { public static void main(String[] args) { String email = “hello2018@gmail.com”; Map<String, Object> env = new HashMap<String, Object>(); env.put(“email”, email); String username = (String) AviatorEvaluator.execute(“email=~/([\w0-8]+)@\w+[\.\w+]+/ ? $1 : ‘unknow’ “, env); System.out.println(username); } }

1 2 3 4 5 6 7 8 9 10 3.11 变量的语法糖衣

/**

  • CreateBy: haleyliu
  • CreateDate: 2018/12/25 */ public class Test { public static void main(String[] args) { User user = new User(1,”jack”,”18”); Map<String, Object> env = new HashMap<>(); env.put(“user”, user);

     String result = (String) AviatorEvaluator.execute(" '[user id='+ user.id + ',name='+user.name + ',age=' +user.age +']' ", env);
     System.out.println(result); } }
    

@Data @AllArgsConstructor @NoArgsConstructor @NoArgsConstructor @ToString public class User {

private int id;

private String name;

private String age;

public User() {
}

}

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 3.12 nil对象[任何对象都比nil大除了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 is null nil与String相加的时候,跟java一样显示为null

1 2 3 4 5 6 7 8 9 3.13 日期比较 public class Test { public static void main(String[] args) { Map<String, Object> env = new HashMap<String, Object>(); final Date date = new Date(); String dateStr = new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss:SS”).format(date); env.put(“date”, date); env.put(“dateStr”, dateStr);

    Boolean result = (Boolean) AviatorEvaluator.execute("date==dateStr",env);
    System.out.println(result);

    result = (Boolean) AviatorEvaluator.execute("date > '2009-12-20 00:00:00:00' ", env);
    System.out.println(result);

    result = (Boolean) AviatorEvaluator.execute("date < '2200-12-20 00:00:00:00' ", env);
    System.out.println(result);

    result = (Boolean) AviatorEvaluator.execute("date ==date ", env);
    System.out.println(result);


} }

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 4 语法手册 4.1 数据类型 Number类型:数字类型,支持两种类型,分别对应Java的Long和Double

任何整数都将被转换为Long

任何浮点数都将被转换为Double,包括用户传入的数值也是如此转换。

不支持科学计数法

支持十进制。如-1、100、2.3等。

整数也可以用十六进制表示,以 0x 或者 0X 开头的数字,比如 0xFF(255)、0xAB(171) 等等。

需要注意,整数相除的结果仍然是整数,比如例子中的 a/b 结果就是 0,遵循 java 的整数运算规则。

String类型: 字符串类型

单引号或者双引号括起来的文本串,如’hello world’

变量如果传入的是String或者Character也将转为String类型。

Bool类型:

常量true和false,表示真值和假值,与java的Boolean.TRUE和Boolean.False对应。

Pattern类型:

类似Ruby、perl的正则表达式,以//括起来的字符串,如//d+/

内部实现为java.util.Pattern。

变量类型:

与Java的变量命名规则相同,

变量的值由用户传入,如”a”、”b”等

nil类型: 常量nil,类似java中的null,

但是nil比较特殊,nil不仅可以参与==、!=的比较,也可以参与>、>=、<、<=的比较,

Aviator规定任何类型都n大于nil除了nil本身,nil==nil返回true。

用户传入的变量值如果为null,那么也将作为nil处理,nil打印为null。

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

4.3 逻辑运算符 Avaitor的支持的逻辑运算符包括,一元否定运算符”!“,以及逻辑与的”&&“,逻辑或的”||”。逻辑运算符的操作数只能为Boolean。

4.4 关系运算符 Aviator支持的关系运算符包括”<” “<=” “>” “>=” 以及”==“和”!=” 。

&&和   都执行短路规则。

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

Aviator规定任何对象都比nil大除了nil之外。

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

4.6 三元运算符 两种模式 默认AviatorEvaluator以编译速度优先:

AviatorEvaluator.setOptimize(AviatorEvaluator.COMPILE); 你可以修改为运行速度优先,这会做更多的编译优化:

AviatorEvaluator.setOptimize(AviatorEvaluator.EVAL);

Search

    微信好友

    博士的沙漏

    Table of Contents