Aviator表达式引擎基本使用

引入依赖

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

AviatorEvaluator.execute()

Aviator只支持4种数值类型:

  1. long
  2. double
  3. bigint
  4. decimal

明确表达式

常量表达式计算

        // 数学运算 (Long)
        String exp1 = "1+2+3";
        Long result = (Long) AviatorEvaluator.execute(exp1);
        System.out.println(result); // 6

        // 数学运算 (Double)
        String exp4 = "1.1+2.2+3.3";
        Double result2 = (Double) AviatorEvaluator.execute(exp4);
        System.out.println(result2); // 6.6


        // 包含关系运算和逻辑运算
        String exp2 = "(1>0||0<1)&&1!=0";
        System.out.println(AviatorEvaluator.execute(exp2)); // true


        // 三元运算
        String exp3 = "4 > 3 ? \"4 > 3\" : 999";
        System.out.println(AviatorEvaluator.execute(exp3)); // 4 > 3

变量表达式计算,map变量传入

        // map 变量传入
        String exp5 = "a>b";
        Map<String, Object> map = new HashMap<>();
        map.put("a", 7);
        map.put("b", 5);
        System.out.println(AviatorEvaluator.execute(exp5, map)); // true

AviatorEvaluator.compile()

可以执行自定义函数调用,为了提升性能,可以先编译表达式,再进行表达式求值。

使用步骤

  1. 自定义函数类
  2. AviatorEvaluator.addFunction() 注册函数类
  3. AviatorEvaluator.compile() 生成Expression对象
  4. Expression.execute() 传入变量值执行获取结果

自定义函数(不可变参数)

  1. 继承 AbstractFunction
  2. 实现 getName(): 定义函数名
  3. 实现 call(): 定义函数逻辑,call()有很多重载方法最多支持20个参数
  4. 在 AviatorEvaluator 注册 (AviatorEvaluator.addFunction())

自定义函数类

public class AviatorFunc extends AbstractFunction {

    /**
     * 实现函数逻辑 
     */
    @Override
    public AviatorObject call(
            Map<String, Object> env,
            AviatorObject arg1,
            AviatorObject arg2) {

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

    /**
     * 定义函数名
     */
    @Override
    public String getName() {
        return "add";
    }

}

定义了一个名为 add 的函数,实现两数相加逻辑

函数调用

        // 自定义函数调用
		AviatorEvaluator.addFunction(new AviatorFunc());
        String exp6 = "add(a,b)";
        Map<String, Object> map2 = new HashMap<>();
        map2.put("a", 7.7d);
        map2.put("b", 5.5d);

        // 缓存字符串表达式
        Expression compileExp = AviatorEvaluator.compile(exp6, true);
        System.out.println(compileExp.execute(map2)); // 13.2

自定义函数(可变参数)

用法与上述类似,但继承 AbstractVariadicFunction

如下:

public class AviatorFuncWithCustomArgs extends AbstractVariadicFunction {

    // 可变形参
    @Override
    public AviatorObject variadicCall(Map<String, Object> map, AviatorObject... args) {
        double sum = 0d;
        for (AviatorObject arg : args) {
            Number a = FunctionUtils.getNumberValue(arg, map);
            sum += a.doubleValue();
        }
        return new AviatorDouble(sum);
    }

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

函数调用:

        // 自定义函数的调用 (可变参数)
        AviatorEvaluator.addFunction(new AviatorFuncWithCustomArgs());
        String exp7 = "customAdd(a,b,c,d)";
        Map<String, Object> map7 = new HashMap<>();
        Double a7 = 7.7d;
        Double b7 = 5.5d;
        Double c7 = 6.6d;
        map7.put("a", a7);
        map7.put("b", b7);
        map7.put("c", c7);
        map7.put("d", 1.1d);

        Expression compileExp7 = AviatorEvaluator.compile(exp7, true);
        System.out.println(compileExp7.execute(map7)); // 20.9

封装工具类

/**
 * Aviator 工具类 应用于规则条件的判断【规则引擎】
 */
public class AviatorUtil {

    /**
     * 解析常量字符串表达式
     *
     * @param str: 字符串表达式,不包含自定义函数, 也不包含变量
     */
    public static Object execute(String str) {
        // 执行AviatorEvaluator 对象的 execute(),获取字符串表达式运算后结果
        return AviatorEvaluator.execute(str);

    }

    /**
     * 解析变量字符串表达式
     *
     * @param str: 字符串表达式, 包含变量
     * @param map: 变量参数
     */
    public static Object execute(
            String str,
            Map<String, Object> map) {

        // 将字符串表达式解析为 Expression 对象
        Expression compileExp = AviatorEvaluator.compile(str, true);
        // 执行Expression 对象的 execute(),获取字符串表达式运算后结果
        return compileExp.execute(map);

    }

    /**
     * 解析自定义函数字符串表达式
     *
     * @param str:  字符串表达式, 包含自定义函数
     * @param func: 自定义函数
     * @return java.lang.Object
     */
    public static Object execute(
            String str,
            AbstractFunction func) {

        // 注册自定义函数
        AviatorEvaluator.addFunction(func);
        // 将字符串表达式解析为 Expression 对象
        Expression compileExp = AviatorEvaluator.compile(str, true);
        // 执行Expression 对象的 execute(),获取字符串表达式运算后结果
        return compileExp.execute();

    }
}

注意!!!

精度丢失问题

使用浮点数计算的过程中可能有精度问题【Java问题】

        AviatorEvaluator.addFunction(new AviatorFuncWithCustomArgs());
        String exp7 = "customAdd(a,b,c)";
        Map<String, Object> map = new HashMap<>();
        Double a = 7.7d;
        Double b = 5.5d;
        Double c = 6.6d;
        map.put("a", a);
        map.put("b", b);
        map.put("c", c);

        Expression compileExp7 = AviatorEvaluator.compile(exp7, true);
        System.out.println(compileExp7.execute(map));

还是上述自定义函数例子,输出结果如下:

image-20231219113741933

使用BigDecimal类改造自定义函数:

public class AviatorFuncWithCustomArgs extends AbstractVariadicFunction {
    
    @Override
    public AviatorObject variadicCall(Map<String, Object> map, AviatorObject... args) {
        return new AviatorDouble(
                Arrays.stream(args)
                        .map(arg -> FunctionUtils.getNumberValue(arg, map))
                        .map(n -> new BigDecimal(n.toString())) // 直接转换为 BigDecimal
                        .reduce(BigDecimal::add)
                        .orElse(BigDecimal.ZERO) // 如果流为空,则返回0
                        .doubleValue() // 如果需要返回double值
        );
    }

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

另一个例子

        String uiExpression = "if(complete / target >= 0.95){ 8 }else{ 0 }";
        Map<String, Object> variable = new HashMap<>();
        variable.put("target", new BigDecimal("100"));
        variable.put("complete", new BigDecimal("95"));

        // 是否跟踪运行,打开后将在控制台打印整个表达式的求值过程。请勿在生产环境打开,将极大地降低性能。默认为 false 关闭。
        AviatorEvaluator.getInstance().setOption(Options.TRACE_EVAL, true);

        // 执行表达式
        final Object execute = AviatorEvaluator.execute(uiExpression, variable);

        System.out.println(execute);

image-20231219115918118

可以看到由于精度问题,导致输出结果0,不是我们想要的8。(0.95 < 0.9500000000000001)

解决方案

AviatorScript 本身有考虑这种计算场景,可以通过配置,强制要求 AviatorScript 框架将整数和浮点数解析为 BigDecimal 类型,而不是坑爹的 Double 类型。

// -- 1. 解析浮点数为 Decimal 类型
AviatorEvaluator.getInstance().setOption(Options.ALWAYS_PARSE_FLOATING_POINT_NUMBER_INTO_DECIMAL, true);
// -- 2. 解析整数为 Decimal 类型
AviatorEvaluator.getInstance().setOption(Options.ALWAYS_PARSE_INTEGRAL_NUMBER_INTO_DECIMAL, true);

添加配置验证:

   		String uiExpression = "if(complete / target >=  0.95){ 8 }else{ 0 }";
        Map<String, Object> variable = new HashMap<>();
        variable.put("target", new BigDecimal("100"));
        variable.put("complete", new BigDecimal("95"));

        // 是否跟踪运行,打开后将在控制台打印整个表达式的求值过程。请勿在生产环境打开,将极大地降低性能。默认为 false 关闭。
        AviatorEvaluator.getInstance().setOption(Options.TRACE_EVAL, true);

        // -- 1. 解析浮点数为 Decimal 类型
        AviatorEvaluator.getInstance().setOption(Options.ALWAYS_PARSE_FLOATING_POINT_NUMBER_INTO_DECIMAL, true);
        // -- 2. 解析整数为 Decimal 类型
        AviatorEvaluator.getInstance().setOption(Options.ALWAYS_PARSE_INTEGRAL_NUMBER_INTO_DECIMAL, true);

        // 执行表达式
        final Object execute = AviatorEvaluator.execute(uiExpression, variable);

        System.out.println(execute);

问题解决,输出结果8:

image-20231219123937778


详细使用参考如下资料:

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不进大厂不改名二号

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值