1、前言
要开发一个高可用系统,对系统的各项性能指标进行监控报警是必不可少的,即系统性能指标满足一定阈值则报警提示。在开发和配置监控指标中必然会使用到规则引擎,主要的规则引擎包括:Drools,EasyRules,Aviator,URule,阿里规则引擎 QLExpress,Groovy等,这里我们先介绍Aviator的一些入门知识。
2、简介
Aviator是一个高性能、轻量级的Java语言实现的表达式求值引擎,主要用于各种表达式的动态求值。现在已经有很多开源可用的Java表达式求值引擎,为什么还需要Aviator呢?
Aviator的设计目标是轻量级和高性能 ,相比于Groovy、JRuby的笨重,Aviator非常小,加上依赖包也才450K,不算依赖包的话只有70K;当然,Aviator的语法是受限的,它不是一门完整的语言,而只是语言的一小部分集合。
其次,Aviator的实现思路与其他轻量级的求值器很不相同,其他求值器一般都是通过解释的方式运行,而Aviator则是直接将表达式编译成Java字节码,交给JVM去执行。简单来说,Aviator的定位是介于Groovy这样的重量级脚本语言和IKExpression这样的轻量级表达式引擎之间。
3、Aviator的特性
- 支持大部分运算操作符,包括算术操作符、关系运算符、逻辑操作符、位运算符、正则匹配操作符(=~)、三元表达式?: ,并且支持操作符的优先级和括号强制优先级,具体请看后面的操作符列表。
- 支持大整数和精度运算(2.3.0版本引入)
- 支持函数调用和自定义函数
- 内置支持正则表达式匹配,类似Ruby、Perl的匹配语法,并且支持类Ruby的$digit指向匹配分组。
- 自动类型转换,当执行操作的时候,会自动判断操作数类型并做相应转换,无法转换即抛异常。
- 支持传入变量,支持类似a.b.c的嵌套变量访问。
- 函数式风格的seq库,操作集合和数组
- 性能优秀
4、Maven依赖
<dependency>
<groupId>com.googlecode.aviator</groupId>
<artifactId>aviator</artifactId>
<version>5.2.7</version>
</dependency>
5、使用
5.1、执行表达式
Aviator的使用方式比较简单,通过使用AviatorEvaluator.execute基本可以实现大部分功能:
public class AviatorTest {
public static void main(String[] args) {
Long result =(Long) AviatorEvaluator.execute("1*2*3");
System.out.println(result);
}
}
这里要注意一个问题,为什么我们的 1*2*3计算过后,要强制转换成Long类型?
因为Aviator只支持四种数字类型(2.3.0之后的版本):Long、Double、big int、decimal
理论上任何整数都将转换成Long(超过Long范围的,将自动转换为big int),任何浮点数都将转换为Double
以大写字母N为后缀的整数都被认为是big int,如1N,2N,9999999999999999999999N等,都是big int类型。
以大写字母M为后缀的数字都被认为是decimal,如1M,2.222M, 100000.9999M等等,都是decimal类型。
5.2、使用变量
变量的使用有两种方法:execute()、exec();
execute(),需要传递Map格式参数
exec(),不需要传递Map
5.2.1、execute
public class AviatorTest {
public static void main(String[] args) {
Map<String, Object> data = Maps.newHashMap();
data.put("username", "yyp");
Object result = AviatorEvaluator.execute("'hello:' + username +'!'", data);
System.out.println(result);
}
}
5.2.2、exec
public class AviatorTest {
public static void main(String[] args) {
String username = "yyp";
Object result = AviatorEvaluator.exec("'hello:' + username +'!'", username);
System.out.println(result);
}
}
5.3、使用函数
Aviator可以使用两种函数:内置函数、自定义函数
5.3.1、使用内置函数
Aviator提供了非常多的内置函数。具体可以查询官方文档参考。
public class AviatorTest {
public static void main(String[] args) {
// sysdate() 获取当前时间Date
// date_to_string(date,format) date转为String
System.out.println(AviatorEvaluator.execute("date_to_string(sysdate(),'yyyy-MM-dd HH:mm:ss')"));
}
}
5.3.2、compile用法
public class AviatorTest {
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", 10);
env.put("b", 4);
env.put("c", 5);
Boolean result = (Boolean) compiledExp.execute(env);
System.out.println(result);
}
}
通过上面的代码片段可以看到,使用compile方法,先生成了 Expression 最后再由Expression.execute,然后传入参数 map,进行计算。
这么做的目的是,在我们实际使用过程中,很多情况下,我们要计算的公式都是一样的,只是每次的参数有所区别。
我们可以把一个编译好的Expression 缓存起来。这样每次可以直接获取我们之前编译的结果直接进行计算,避免Perm区OutOfMemory
Aviator本身自带一个全局缓存,如果决定缓存本次的编译结果,只需要
Expression compiledExp = AviatorEvaluator.compile(expression,true);
这样设置后,下一次编译同样的表达式,Aviator会从全局缓存中,拿出已经编译好的结果,不需要动态编译。如果需要使缓存失效,可以使用
AviatorEvaluator.invalidateCache(String expression);
5.3.3、自定义函数
自定义函数需要继承AbstractFunction类,重写目标方法。
public class AviatorTest {
public static void main(String[] args) {
// 注册
AviatorEvaluator.addFunction(new AddFunction());
// 方式1
System.out.println(AviatorEvaluator.execute("add(12.23, -2.3)"));
// 方式2
Map<String, Object> params = new HashMap<String, Object>();
params.put("a", 12.23);
params.put("b", -2.3);
System.out.println(AviatorEvaluator.execute("add(a, b)", params));
}
static class AddFunction extends AbstractFunction {
@Override
public String getName() {
return "add";
}
@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);
}
}
}
我们看到:
- 继承AbstractFunction类,就可以自定义一个函数
- 重写call方法,就可以定义函数的逻辑,可以通过FunctionUtils获取脚本传递的参数
- 通过getName可以设置函数的名称
- 通过addFunction添加一个自定义函数类的实例,就可以注册函数
- 最后就可以在Aviator的脚本里编译执行我们自定义的函数
总结
Aviator 表达式引擎是一个轻量级的 Java 表达式求值框架,具有高性能、易用性和可扩展性等特点。它可以在项目中高效地解析和计算表达式,并支持自定义函数和操作符的扩展。在实际应用中,合理利用 Aviator 的缓存机制、灵活使用函数和操作符等最佳实践,可以提高表达式求值的性能和可维护性。
本文暂时没有评论,来添加一个吧(●'◡'●)