Aviator计算引擎-我们研发团队的最佳实践(上篇)概述

[toc]

概述

Aviator是一个开源的Java表达式求值器,不仅支持四则运算、三元运算、逻辑运算,而且其强大的接口支持自定义扩展函数。鉴于此,我原先的研发团队结合公司业务场景,选择了这个google的计算引擎,为了扩展我们业务需求,定义了一系列自定义函数,以支撑我们业务场景。

源代码

特性

数据类型

  • Number类型:数字类型,支持两种类型,分别对应Java的Long和Double,也就是说任何整数都将被转换为Long,而任何浮点数都将被转换 为Double,包括用户传入的数值也是如此转换。不支持科学计数法,仅支持十进制。如-1、100、2.3等。
  • String类型:字符串类型,单引号或者双引号括起来的文本串,如’helloworld’,变量如果传入的是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。

操作类型

算术运算符

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

逻辑运算符

Avaitor的支持的逻辑运算符包括,一元否定运算符"!",以及逻辑与的"&&",逻辑或的"||"。逻辑运算符的操作数只能为Boolean。 关系运算符
Aviator支持的关系运算符包括"<" “<=” “>” “>=” 以及"==“和”!=" 。 &&和||都执行短路规则。
关系运算符可以作用于Number之间、String之间、Pattern之间、Boolean之间、变量之间以及其他类型与nil之间的关系比较,不同类型除了nil之 外不能相互比较。
Aviator规定任何对象都比nil大除了nil之外。

位运算符

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

匹配运算符

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

三元运算符

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

扩展特性

数字扩展

1、多个数字求和:sum

对多个数字进行求和,忽略空值,作为0处理,提供兼容。

示例代码:

/**
 * @description: 多个数字求和计算(其中任何一个数字为空则作为0处理)
 * @Date : 2021/4/11 9:43 AM
 * @Author : 石冬冬-Seig Heil
 */
public class SumTest {
    @Test
    public void test(){
        Map<String,Object> evn = new HashMap<String,Object>(){{
            put("x",2);
            put("y",4);
            put("z",8);
        }};
        System.out.println(AviatorExecutor.executeBigDecimal(AviatorContext.builder().expression("sum(x,y,z,h)").env(evn).build()));
    }
}
复制代码

输出:

14
复制代码

2、多个数字获取最大值:max

对多个数字获取最大值,忽略空值,作为0处理,提供兼容。

示例代码:

/**
 * @description: 多个数字求最大值(其中任何一个数字为空则作为0处理)
 * @Date : 2021/4/11 9:43 AM
 * @Author : 石冬冬-Seig Heil
 */
public class MaxTest {
    @Test
    public void test(){
        Map<String,Object> evn = new HashMap<String,Object>(){{
            put("x",2);
            put("y",4);
            put("z",8);
        }};
        System.out.println(AviatorExecutor.executeBigDecimal(AviatorContext.builder().expression("max(x,y,z,h)").env(evn).build()));
    }
}
复制代码

输出:

8
复制代码

3、多个数字获取最大值:min

对多个数字获取最小值,忽略空值,作为0处理,提供兼容。

示例代码:

/**
 * @description: 多个数字求最小值(其中任何一个数字为空则作为0处理)
 * @Date : 2021/4/11 9:43 AM
 * @Author : 石冬冬-Seig Heil
 */
public class MinTest {
    @Test
    public void test(){
        Map<String,Object> evn = new HashMap<String,Object>(){{
            put("x",2);
            put("y",4);
            put("z",8);
        }};
        System.out.println(AviatorExecutor.executeBigDecimal(AviatorContext.builder().expression("min(x,y,z,h)").env(evn).build()));
    }
}
复制代码

输出:

2
复制代码

4、向上取整:ceil

向上取整

示例代码:

/**
 * @description: 向上取整
 * @Date : 2021/4/11 9:43 AM
 * @Author : 石冬冬-Seig Heil
 */
public class CeilTest {

    @Test
    public void test(){

        System.out.println(AviatorExecutor.executeBigDecimal(AviatorContext.builder().expression("ceil(1.2)").env(Collections.emptyMap()).build()));

        System.out.println(AviatorExecutor.executeBigDecimal(AviatorContext.builder().expression("ceil(1.6)").env(Collections.emptyMap()).build()));

        System.out.println(AviatorExecutor.executeBigDecimal(AviatorContext.builder().expression("ceil(201)").env(Collections.emptyMap()).build()));

        System.out.println(AviatorExecutor.executeBigDecimal(AviatorContext.builder().expression("ceil(196)").env(Collections.emptyMap()).build()));
    }
}
复制代码

输出:

2.0
2.0
201.0
196.0
复制代码

5、向下取整:floor

向上取整

示例代码:

/**
 * @description: 向下取整
 * @Date : 2021/4/11 9:43 AM
 * @Author : 石冬冬-Seig Heil
 */
public class FloorTest {

    @Test
    public void test(){

        System.out.println(AviatorExecutor.executeBigDecimal(AviatorContext.builder().expression("floor(1.2)").env(Collections.emptyMap()).build()));

        System.out.println(AviatorExecutor.executeBigDecimal(AviatorContext.builder().expression("floor(1.6)").env(Collections.emptyMap()).build()));

        System.out.println(AviatorExecutor.executeBigDecimal(AviatorContext.builder().expression("floor(201)").env(Collections.emptyMap()).build()));

        System.out.println(AviatorExecutor.executeBigDecimal(AviatorContext.builder().expression("floor(196)").env(Collections.emptyMap()).build()));
    }
}
复制代码

输出:

1.0
1.0
201.0
196.0
复制代码

6、四舍五入:round

四舍五入

示例代码:

/**
 * @description: 四舍五入
 * @Date : 2021/4/11 9:43 AM
 * @Author : 石冬冬-Seig Heil
 */
public class RoundTest {

    @Test
    public void test(){

        System.out.println(AviatorExecutor.executeBigDecimal(AviatorContext.builder().expression("round(1.2)").env(Collections.emptyMap()).build()));

        System.out.println(AviatorExecutor.executeBigDecimal(AviatorContext.builder().expression("round(1.6)").env(Collections.emptyMap()).build()));

        System.out.println(AviatorExecutor.executeBigDecimal(AviatorContext.builder().expression("round(201)").env(Collections.emptyMap()).build()));

        System.out.println(AviatorExecutor.executeBigDecimal(AviatorContext.builder().expression("round(196)").env(Collections.emptyMap()).build()));
    }
}
复制代码

输出:

1
2
201
196
复制代码

7、精度处理:scale

精度处理,具体参数说明查看 org.suze.aviator.function.number.Scale

示例代码:

/**
 * @description:  对数字取整方式 和 精度进位 处理
 * @Date : 2021/4/11 9:43 AM
 * @Author : 石冬冬-Seig Heil
 */
public class ScaleTest {

    @Test
    public void test(){
        Map<String,Object> evn = new HashMap<String,Object>(){{
            put("x",15.344);
        }};
        System.out.println(AviatorExecutor.executeBigDecimal(AviatorContext.builder().expression("scale(x,0,0)").env(evn).build()));
        System.out.println(AviatorExecutor.executeBigDecimal(AviatorContext.builder().expression("scale(x,0,1)").env(evn).build()));
        System.out.println(AviatorExecutor.executeBigDecimal(AviatorContext.builder().expression("scale(x,0,2)").env(evn).build()));
        System.out.println(AviatorExecutor.executeBigDecimal(AviatorContext.builder().expression("scale(x,-2,1)").env(evn).build()));
        System.out.println(AviatorExecutor.executeBigDecimal(AviatorContext.builder().expression("scale(x,-2,2)").env(evn).build()));
    }
}
复制代码

输出:

15
16
15
100
0
复制代码

集合扩展

1、判断一个字符串是否在一个字符串区间集合中:in(finder,delimiter,values)

判断一个字符串是否在一个字符串区间集合中,finder代表查找项,delimiter字符串分隔符,values以delimiter分割的字符串。

示例代码:

/**
 * @description: 判断一个字符串是否在一个字符串区间集合中
 * @Date : 2021/4/11 9:43 AM
 * @Author : 石冬冬-Seig Heil
 */
public class InTest {
    @Test
    public void test(){
        Map<String,Object> evn = new HashMap<String,Object>(){{
            put("x",15);
        }};
        String expression = "in(x, ',' , '15,34,22')";
        AviatorContext ctx = AviatorContext.builder().expression(expression).env(evn).build();
        System.out.println(AviatorExecutor.executeBoolean(ctx));
    }
}
复制代码

输出:

true
复制代码

2、判断一个字符串是否不在一个字符串区间集合中:notin(finder,delimiter,values)

判断一个字符串是否不在一个字符串区间集合中,finder代表查找项,delimiter字符串分隔符,values以delimiter分割的字符串。

示例代码:

/**
 * @description: 判断一个字符串是否在一个字符串区间集合中
 * @Date : 2021/4/11 9:43 AM
 * @Author : 石冬冬-Seig Heil
 */
public class InTest {
    @Test
    public void test(){
        Map<String,Object> evn = new HashMap<String,Object>(){{
            put("x",15);
        }};
        String expression = "notin(x, ',' , '15,34,22')";
        AviatorContext ctx = AviatorContext.builder().expression(expression).env(evn).build();
        System.out.println(AviatorExecutor.executeBoolean(ctx));
    }
}
复制代码

输出:

false
复制代码

通用函数

基于日常业务场景需求,扩展的相关自定义函数。

1、两日期相差天数,参数类型为date:days.diff(current,destination)

两日期相差天数,参数类型为date

示例代码:

/**
 * @description: 两日期相差天数,参数类型为date
 * @Date : 2021/4/11 10:19 AM
 * @Author : 石冬冬-Seig Heil
 */
public class DiffDaysTest {

    @SneakyThrows
    @Test
    public void test(){
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        Map<String,Object> evn = new HashMap<String,Object>(){{
            put("current",simpleDateFormat.parse("2021-04-11"));
            put("destination",simpleDateFormat.parse("2020-04-11"));
        }};
        String expression = "days.diff(current,destination)";
        AviatorContext ctx = AviatorContext.builder().expression(expression).env(evn).build();
        System.out.println( AviatorExecutor.executeBigDecimal(ctx));
    }
}
复制代码

输出:

365
复制代码

2、获取生日年龄:age(date)

根据出生年月日计算年龄,出生年月日类型为字符串

示例代码:

/**
 * @description: 根据出生年月日计算年龄,出生年月日类型为字符串
 * @Date : 2021/4/11 10:19 AM
 * @Author : 石冬冬-Seig Heil
 */
public class AgeTest {

    @SneakyThrows
    @Test
    public void test(){
        Map<String,Object> evn = new HashMap<String,Object>(){{
            put("birth","19880808");
        }};
        String expression = "age(birth)";
        AviatorContext ctx = AviatorContext.builder().expression(expression).env(evn).build();
        System.out.println("age:" + AviatorExecutor.execute(ctx));
    }
}

复制代码

输出:

32
复制代码

3、截取字符串中的数字求和:string.numbers.sum

截取字符串中的数字求和

示例代码:

/**
 * @description: 截取字符串中的数字求和
 * @Date : 2021/4/11 10:19 AM
 * @Author : 石冬冬-Seig Heil
 */
public class StringNumbersSumTest {

    @SneakyThrows
    @Test
    public void test(){
        String str = "保费借款金额:人民币(第一年:5600.00元;第二年:1110.00元;第三年:1000.00元)";
        Map<String,Object> evn = new HashMap<String,Object>(){{
            put("str",str);
        }};
        String expression = "string.numbers.sum(str, '-', ':', '元')";
        AviatorContext ctx = AviatorContext.builder().expression(expression).env(evn).cached(false).build();
        System.out.println(AviatorExecutor.execute(ctx));
    }
}
复制代码

输出:

7710.00
复制代码

4、数字转大写金额:chinese.number.upper(num)

截取字符串中的数字求和

示例代码:

/**
 * @description: 数字转大写金额
 * @Date : 2021/4/11 10:19 AM
 * @Author : 石冬冬-Seig Heil
 */
public class ChineseNumberUpperTest {

    @SneakyThrows
    @Test
    public void test(){
        BigDecimal num = new BigDecimal("11111.01");
        Map<String,Object> evn = new HashMap<String,Object>(){{
            put("num",num);
        }};
        String expression = "chinese.number.upper(num)";
        AviatorContext ctx = AviatorContext.builder().expression(expression).env(evn).build();
        System.out.println(AviatorExecutor.execute(ctx));
    }
}

复制代码

输出:

壹万壹仟壹佰壹拾壹元零壹分
复制代码

对象函数

为空时,设置一个默认值

1、为空时,设置一个默认值:nvl(value,defaultValue)

两日期相差天数,参数类型为date

示例代码:

/**
 * @description: 根据出生年月日计算年龄,出生年月日类型为字符串
 * @Date : 2021/4/11 10:19 AM
 * @Author : 石冬冬-Seig Heil
 */
public class NvlTest {

    @SneakyThrows
    @Test
    public void test(){
        String expression = "nvl(a,0)";
        AviatorContext ctx = AviatorContext.builder().expression(expression).env(Collections.emptyMap()).build();
        System.out.println(AviatorExecutor.executeBigDecimal(ctx));
    }
}
复制代码

输出:

0
复制代码

2、字符串转数字:objects.toNumber(value,defaultValue)

字符串转数字,如果字符串为空,则使用默认值作为返回值,objects.toNumber(value,defaultValue)

示例代码:

/**
 * @description: objects.toNumber(x,y)
 * @Date : 2021/4/11 10:19 AM
 * @Author : 石冬冬-Seig Heil
 */
public class ToNumberTest {

    @SneakyThrows
    @Test
    public void test(){
        String expression = "objects.toNumber('1',0)";
        AviatorContext ctx = AviatorContext.builder().expression(expression).env(Collections.emptyMap()).build();
        System.out.println(AviatorExecutor.executeBigDecimal(ctx));
    }
}
复制代码

输出:

1
复制代码

3、数字转字符串:objects.toString(value,defaultValue)

数字转字符串,如果数字为空,则使用默认值作为返回值,objects.toString(value,defaultValue)

示例代码:

/**
 * @description: 数字转字符串,如果数字为空,则使用默认值作为返回值,objects.toString(value,defaultValue)
 * @Date : 2021/4/11 10:19 AM
 * @Author : 石冬冬-Seig Heil
 */
public class ToStringTest {

    @SneakyThrows
    @Test
    public void test(){
        String expression = "objects.toString(1,'0')";
        AviatorContext ctx = AviatorContext.builder().expression(expression).env(Collections.emptyMap()).build();
        System.out.println(AviatorExecutor.executeBigDecimal(ctx));
    }
}
复制代码

输出:

1
复制代码

高级特性

示例代码:

/**
 * @description: 基于Aviator测试类
 * @Date : 2018/9/7 下午6:00
 * @Author : 石冬冬-Seig Heil(shiyongxin2010@163.com)
 */
@Slf4j
public class AviatorObjectTest {

    @Test
    public void object_instance(){
        Teacher teacher = new Teacher(){{
            setName("im");
            setScore(20);
        }};

        Student student = new Student(){{
            setName("Jack");
            setScore(22);
        }};
        Map<String,Object> env = new HashMap<String,Object>(){{
            put("teacher",teacher);
            put("student",student);
        }};

        Object value = AviatorExecutor.execute(AviatorContext.builder().env(env).expression("student.name").build());
        System.out.println(value);
    }

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class Colleague {
        private String name;
        private double score;
    }

    @Data
    @NoArgsConstructor
    public class Student extends Colleague {

        public Student(String name, double score) {
            super(name, score);
        }
    }

    @Data
    @NoArgsConstructor
    public class Teacher extends Colleague {
    }

}
复制代码

输出:

Jack
复制代码

扩展阅读

CSDN 介绍
掘金 介绍
Aviator 源码地址
Aviator api 相关文档