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 {/*** 实现函数逻辑 */@Overridepublic 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());}/*** 定义函数名*/@Overridepublic 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 {// 可变形参@Overridepublic 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);}@Overridepublic 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 {@Overridepublic 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值);}@Overridepublic 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


详细使用参考如下资料:

  • https://juejin.cn/post/7273032322546647096
  • AviatorScript 编程指南(5.0)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/286422.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

自学java马上入职怕干不了活怎么办?

自学java马上入职怕干不了活怎么办&#xff1f; 在开始前我有一些资料&#xff0c;是我根据自己从业十年经验&#xff0c;熬夜搞了几个通宵&#xff0c;精心整理了一份「java的资料从专业入门到高级教程工具包」&#xff0c;点个关注&#xff0c;全部无偿共享给大家&#xff01…

Java_异常

一、异常 1.1 认识异常 接下来&#xff0c;我们学习一下异常&#xff0c;学习异常有利于我们处理程序中可能出现的问题。我先带着同学们认识一下&#xff0c;什么是异常&#xff1f; 我们阅读下面的代码&#xff0c;通过这段代码来认识异常。 我们调用一个方法时&#xff0c…

论文Rebuttal常见格式与模板之中篇

论文Rebuttal常见格式与模板之中篇 前言5. Rebuttal可能遇到的问题5.4 实验不充分5.5 语法&#xff0c;结构&#xff0c;参考文献遗漏等问题5.6 非热点问题&#xff0c;研究意义&#xff1f; 6. 针对AC Message下篇笔记链接Rebuttal模板的好文 前言 这里承接上一篇笔记&#x…

opencv 入门一(显示一张图片)

头文件添加如下&#xff1a; 库目录添加如下&#xff1a; 依赖的库如下&#xff1a; #include <iostream> #include "opencv2/opencv.hpp" int main(int argc,char ** argv) { cv::Mat img cv::imread(argv[1], -1); if (img.empty()) return -1; …

原生JS实现组件切换(不刷新页面)

这是通过原生Es6实现的组件切换&#xff0c;代码很简单&#xff0c;原理和各种框架原理大致相同。 创建文件 ├── component&#xff1a;存放组件 │ ├── home1.js&#xff1a;组件1 │ ├── home2.js&#xff1a;组件2 ├── index.html ├── index.js初始化ht…

git的使用思维导图

源文件在github主页&#xff1a;study_collection/cpp学习/git at main stu-yzZ/study_collection (github.com)

全网好听的BGM都在这里下载,赶紧收藏好了

无论是自媒体创作者还是从事视频剪辑工作的朋友&#xff0c;对于BGM的选择都很重要&#xff0c;一首适配的BGM能大大提升你作品的质量&#xff0c;还能让作品更优秀。哪里才能找到好听又免费的BGM&#xff1f;下面推荐几个我多年收藏的6个音效、音频素材网站&#xff0c;赶紧收…

jdk多版本切换环境变量管理(jdk1.8和jdk17)

jdk多版本切换环境变量管理&#xff08;jdk1.8和jdk17&#xff09; 看了很多网上的博客&#xff0c;根本都不行&#xff0c;我总结出来规律如下&#xff1a; 首先环境变量要配置成这个样子&#xff1a;这些博客都会教你们配 接着配什么classpath&#xff0c;看其他博客就行 还…

鸿蒙开发之简单登录页面

Entry Component struct Index {State loading:booleanfalse;build() {Row() {Column({ space: 5 }) {Image($r("app.media.app_icon")).width(100).height(100).borderRadius(10).margin({top: 60})Text("登录界面").fontSize(40).fontWeight(FontWeight.…

Jmeter基础和概念(超详细整理)

JMeter 介绍&#xff1a; 一个非常优秀的开源的性能测试工具。 优点&#xff1a;你用着用着就会发现它的重多优点&#xff0c;当然不足点也会呈现出来。 从性能工具的原理划分&#xff1a; Jmeter工具和其他性能工具在原理上完全一致&#xff0c;工具包含4个部分&#xff1a…

javascript--深拷贝,浅拷贝区别

浅拷贝和深拷贝都是创建一份数据的拷贝。 js分为原始类型和引用类型&#xff0c;对于原始类型的拷贝&#xff0c;并没有深浅拷贝的区别&#xff0c;只有拷贝引用类型的时候存在深浅拷贝的区别的问题。 浅拷贝只进行一层复制&#xff0c;引用类型还是共享内存地址。 深拷贝是无…

原知因,正成为中产群体“精准抗衰”新选择

惧怕衰老&#xff0c;渴望长寿&#xff0c;是全人类的共同属性。而在现代生命科学的加持下&#xff0c;科学家们的勇气也前所未有的可畏&#xff0c;人类长寿基因发现者Nir Barzilai就是其中最具代表性的一位。作为人类史上首个抗衰老临床研究TAME的负责人&#xff0c;这位大佬…