Aviator表达式引擎基本使用
2023-12-19 21:36:49
引入依赖
<dependency>
<groupId>com.googlecode.aviator</groupId>
<artifactId>aviator</artifactId>
<version>5.3.3</version>
</dependency>
AviatorEvaluator.execute()
Aviator只支持4种数值类型:
- long
- double
- bigint
- 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()
可以执行自定义函数调用,为了提升性能,可以先编译表达式,再进行表达式求值。
使用步骤
- 自定义函数类
- AviatorEvaluator.addFunction() 注册函数类
- AviatorEvaluator.compile() 生成Expression对象
- Expression.execute() 传入变量值执行获取结果
自定义函数(不可变参数)
- 继承
AbstractFunction
- 实现 getName(): 定义函数名
- 实现 call(): 定义函数逻辑,call()有很多重载方法最多支持20个参数
- 在 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));
还是上述自定义函数例子,输出结果如下:
使用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);
可以看到由于精度问题,导致输出结果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:
详细使用参考如下资料:
- https://juejin.cn/post/7273032322546647096
- AviatorScript 编程指南(5.0)
文章来源:https://blog.csdn.net/qq_43417581/article/details/135084417
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!