发布时间:2025-12-10 11:47:33 浏览次数:20
Aviator是一门高性能、轻量级的Java语言实现的表达式动态求值引擎。其设计目标是轻量级和高性能,相对于Groovy、JRuby的笨重,Aviator非常小,不过Aviator的语法受限,它并不是一门完整的语言,只是语言的一小部分集合。定位是介于Groovy这样重量级脚本语言和IKExpression这样轻量级表达式引擎之间。
Aviator的实现思路与其它轻量级的求值器不同,其它求值器是通过解释的方式运行,而Aviator是直接将表达式编译成Java字节码,交给JVM去执行。
功能
限制:没有if else、do while等语句,没有赋值语句,没有位运算符;
使用场景
pom文件引入:
<dependency><groupId>com.googlecode.aviator</groupId><artifactId>aviator</artifactId></dependency>Aviator的数值类型只支持Long和Double,任何整数都将会转换成Long,任何浮点数都会转换成Double,包括用户传入的变量数值。不支持科学计数法,仅支持十进制。
nil对象
nil是Aviator内置的常量,类似Java中的null,空值。nil跟null不同在于,Java中null只能使用在==、!=的比较运算符,而nil还可以使用>、>=、<、<=等比较运算符。Aviator规定,任何对象都比nil大,除nil本身。用户传入的变量如果为null,将自动以nil替代。nil与String相加时,跟Java一样显示为null。
Aviator并不支持日期类型,如果要比较日期,你需要将日期写字符串的形式,并且要求是形如yyyy-MM-dd HH:mm:ss:SS的字符串,否则都将报错。字符串跟java.util.Date比较时将自动转换为Date对象进行比较。
要访问变量a中的某个属性b,可通过a.b访问到,即Aviator支持变量的嵌套访问。
Aviator在表达式级别支持正则表达式,通过//括起来的字符序列构成一个正则表达式,可用于匹配(作为=~操作符的右操作数)、比较大小,匹配仅能与字符串进行匹配。匹配成功后,Aviator会自动将匹配成功的分组放入$num的变量中,$0指代整个匹配的字符串,$1表示第一个分组,以此类推。正则表达式规则跟Java完全一样,内部使用java.util.regex.Pattern编译。
类型转换规则
访问数组和集合
可以通过中括号去访问数组和java.util.List对象,可以通过map.key访问java.util.Map中key对应的value。
两种模式
依赖的实体Bean类为:
@Data@AllArgsConstructorprivate static class Foo { int i;Date date;}| sysdate() | 返回当前日期对象java.util.Date |
| rand() | 返回一个介于0-1的随机数,double类型 |
| print([out],obj) | 打印对象,如果指定out,向out打印,否则输出到控制台 |
| now() | 返回System.currentTimeMillis |
| string.substring(s,begin[,end]) | 截取字符串s,从begin到end,end如果忽略的话,将从begin到结尾,与java.util.String.substring一样。 |
| math.log(d) | 求d的自然对数 |
| math.log10(d) | 求d以10为底的对数 |
| map(seq,fun) | 将函数fun作用到集合seq每个元素上,返回新元素组成的集合 |
| filter(seq,predicate) | 将谓词predicate作用在集合的每个元素上,返回谓词为true的元素组成的集合 |
| include(seq,element) | 判断element是否在集合seq中,返回boolean值 |
| sort(seq) | 排序集合,仅对数组和List有效,返回排序后的新集合 |
| reduce(seq,fun,init) | fun接收两个参数,集合元素,累积init,用于将fun作用在集合每个元素和初始值上面,返回最终的init值 |
| seq.eq(value) | 返回一个谓词,用来判断传入的参数是否跟value相等,一般用于filter函数,如filter(seq,seq.eq(3)) 过滤返回等于3的元素组成的集合 |
| seq.exists() | 返回判断存在,即不为nil的谓词 |
实现com.googlecode.aviator.runtime.function.AbstractFunction接口,并注册到AviatorEvaluator即可使用
public class UserDefinedFunction extends AbstractFunction {/*** 自定义函数名称*/@Overridepublic String getName() {return "add";}@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());}}使用自定义函数之前需要通过addFunction()方式注册,否则报错com.googlecode.aviator.exception.FunctionNotFoundException: Function not found: add。
// 注册函数AviatorEvaluator.addFunction(new UserDefinedFunction());log.info("add(1, 1) = " + AviatorEvaluator.execute("add(1, 1)"));AviatorEvaluator.removeFunction("add");// log.info("add(1, 1) = " + AviatorEvaluator.execute("add(1, 1)"));每次执行Aviator.execute()时,背后都经过编译和执行的操作。那可以先编译表达式,拿到编译后结果,然后传入不同的env来重复使用编译结果,提高性能。
/*** 编译表达式和未编译表达式性能测试*/@Testpublic void testCompile() {String expression = "a * (b - c)";Map<String, Object> env = new HashMap<>();env.put("a", 3.3);env.put("b", 2.2);env.put("c", 1.1);int num = 10000;// 编译表达式Expression compliedExp = AviatorEvaluator.compile(expression);Stopwatch watch = Stopwatch.createStarted();for (int i = 0; i < num; i++) {compliedExp.execute(env);}log.info(String.format("预编译耗时为%dms", watch.elapsed(TimeUnit.MILLISECONDS)));watch.stop().start();for (int i = 0; i < num; i++) {AviatorEvaluator.execute(expression, env);}log.info(String.format("无编译耗时为%dms", watch.elapsed(TimeUnit.MILLISECONDS)));}输出:
ExpressionTest [testCompile:65] 预编译耗时为13msExpressionTest [testCompile:70] 无编译耗时为2435ms通过complie方法可以将表达式编译成Expression的中间对象,当要执行表达式时传入Map对象直接调用Expression的execute方法即可。
编译后结果可以自己缓存,也可以交给Aviator缓存,AviatorEvaluator内部维护有一个全局缓存池:
/*** 预编译缓存举例*/@Testpublic void testCompileCache() {String expression1 = "a + b + c";Expression exp1 = AviatorEvaluator.compile(expression1, true);Expression exp2 = AviatorEvaluator.compile(expression1, true);Expression exp3 = AviatorEvaluator.compile(expression1, false);log.info("exp1 == exp2 : " + (exp1 == exp2));log.info("exp1 == exp3 : " + (exp1 == exp3));}输出:
ExpressionTest [testCompileCache:79] exp1 == exp2: trueExpressionTest [testCompileCache:80] exp1 == exp3: false将cached设置为true时,那下次编译同一个表达式时将直接返回上一次编译结果。使缓存失效:public static void invalidateCache(String expression)。
没有规则引擎时,有些逻辑复杂的业务代码,只能通过不断的增添if - else来满足复杂的业务场景,当if - else过多时,会导致代码极难阅读,虽然能通过策略模式来优化if - else,但依旧存在开发周期缓慢、发布上线的问题。
所以需要规则引擎,尤其是风控系统。
规则引擎的优势:
Aviator也可用于规则引擎
将业务人员配置的规则转换成一个规则字符串,然后将该规则字符串保存进数据库中,当使用该规则时,只传递该规则所需要的参数,便可以直接计算出结果,开发人员无需再为这些规则编写任何代码。
输出:
公式1:false公式2:true