java aviator_Aviator——让表达式飞起来

article/2025/10/24 0:24:49

《飞行大亨》是我很喜欢的一部电影,不过这里我想介绍的是一个叫Aviator的开源的Java表达式求值器。

一、轮子的必要性

表达式的求值上,java的选择非常多,强大的如Groovy、JRuby,N年没维护的beanshell,包括javaeye上朋友的IKExpression。为什么还需要Aviator?或者说Aviator的特点是什么?

我将Aviator定位在Groovy这样全功能的脚本和IKExpression这样的简易的表达式求值之间的东西,如果你不希望带上Groovy那么庞大的jar却只用上一点点的功能,如果你希望功能和性能上比IKExpression好那么一些,那么也许你可以考虑Aviator。

Aviator的设计思路跟利用GroovyObject的求值是一样,通过编译并动态生成字节码的方式将表达式编译成一个类,然后反射执行这个类,因此会在效率上比纯解释执行的IKExpression好一些。

b8b17b08982b16eff85e38b6679a0270.png

二、让轮子转起来。

求算术表达式:

4f1150b881333f12a311ae9ef34da474.pngimportcom.googlecode.aviator.AviatorEvaluator;

4f1150b881333f12a311ae9ef34da474.png

4f1150b881333f12a311ae9ef34da474.png

1fa987a29c6482f53d401256f96355eb.png

ca75c07623e1b494fee67e8f316fc310.gifpublicclassSimpleExample9b8a8a44dd1c74ae49c20a7cd451974e.png{

97e794c86028c5f5b5461ae5ef440a4c.png

3c6cafce68eb941a00f1998f1d3d3aa6.gifpublicstaticvoidmain(String[] args)9b8a8a44dd1c74ae49c20a7cd451974e.png{

d18c02628675d0a2c816449d98bda930.png        Long result=(Long) AviatorEvaluator.execute("1+2+3");

d18c02628675d0a2c816449d98bda930.png        System.out.println(result);

ecedf933ec37d714bd4c2545da43add2.png    }8f1ba5b45633e9678d1db480c16cae3f.png}

执行入口统一为AviatorEvaluator类,它有一系列静态方法。

逻辑表达式和关系运算:

4f1150b881333f12a311ae9ef34da474.pngAviatorEvaluator.execute("3>1 && 2!=4 || true");

Aviator支持所有的关系运算符和算术运算符,不支持位运算,同时支持表达式的优先级,优先级跟Java的运算符一样,并且支持通过括号来强制优先级。

使用变量和字符串相加:

4f1150b881333f12a311ae9ef34da474.pngString yourname=“aviator”;

4f1150b881333f12a311ae9ef34da474.png        Mapenv=newHashMap();

4f1150b881333f12a311ae9ef34da474.png        env.put("yourname", yourname);

4f1150b881333f12a311ae9ef34da474.png        String result=(String) AviatorEvaluator.execute("'hello ' + yourname", env);

4f1150b881333f12a311ae9ef34da474.png        System.out.println(result);

打印:

4f1150b881333f12a311ae9ef34da474.pnghello aviator

字符串可以单引号也可以双引号括起来,并且支持转义字符。变量名称只要是合法的java identifer即可,变量需要用户传入,通过Map指定变量名和值是什么,这里的变量是yourname。

变量的访问支持嵌套访问,也就是dot操作符来访问变量里的属性,假设我们有一个Foo类:

1fa987a29c6482f53d401256f96355eb.png

ca75c07623e1b494fee67e8f316fc310.gifpublicstaticclassFoo9b8a8a44dd1c74ae49c20a7cd451974e.png{

d18c02628675d0a2c816449d98bda930.pnginti;

d18c02628675d0a2c816449d98bda930.pngfloatf;

d18c02628675d0a2c816449d98bda930.png        Date date=newDate();

d18c02628675d0a2c816449d98bda930.png

97e794c86028c5f5b5461ae5ef440a4c.png

3c6cafce68eb941a00f1998f1d3d3aa6.gifpublicFoo(inti,floatf, Date date)9b8a8a44dd1c74ae49c20a7cd451974e.png{

d18c02628675d0a2c816449d98bda930.pngsuper();

d18c02628675d0a2c816449d98bda930.pngthis.i=i;

d18c02628675d0a2c816449d98bda930.pngthis.f=f;

d18c02628675d0a2c816449d98bda930.pngthis.date=date;

ecedf933ec37d714bd4c2545da43add2.png        }97e794c86028c5f5b5461ae5ef440a4c.png

3c6cafce68eb941a00f1998f1d3d3aa6.gifpublicintgetI()9b8a8a44dd1c74ae49c20a7cd451974e.png{

d18c02628675d0a2c816449d98bda930.pngreturni;

ecedf933ec37d714bd4c2545da43add2.png        }97e794c86028c5f5b5461ae5ef440a4c.png

3c6cafce68eb941a00f1998f1d3d3aa6.gifpublicvoidsetI(inti)9b8a8a44dd1c74ae49c20a7cd451974e.png{

d18c02628675d0a2c816449d98bda930.pngthis.i=i;

ecedf933ec37d714bd4c2545da43add2.png        }97e794c86028c5f5b5461ae5ef440a4c.png

3c6cafce68eb941a00f1998f1d3d3aa6.gifpublicfloatgetF()9b8a8a44dd1c74ae49c20a7cd451974e.png{

d18c02628675d0a2c816449d98bda930.pngreturnf;

ecedf933ec37d714bd4c2545da43add2.png        }97e794c86028c5f5b5461ae5ef440a4c.png

3c6cafce68eb941a00f1998f1d3d3aa6.gifpublicvoidsetF(floatf)9b8a8a44dd1c74ae49c20a7cd451974e.png{

d18c02628675d0a2c816449d98bda930.pngthis.f=f;

ecedf933ec37d714bd4c2545da43add2.png        }97e794c86028c5f5b5461ae5ef440a4c.png

3c6cafce68eb941a00f1998f1d3d3aa6.gifpublicDate getDate()9b8a8a44dd1c74ae49c20a7cd451974e.png{

d18c02628675d0a2c816449d98bda930.pngreturndate;

ecedf933ec37d714bd4c2545da43add2.png        }97e794c86028c5f5b5461ae5ef440a4c.png

3c6cafce68eb941a00f1998f1d3d3aa6.gifpublicvoidsetDate(Date date)9b8a8a44dd1c74ae49c20a7cd451974e.png{

d18c02628675d0a2c816449d98bda930.pngthis.date=date;

ecedf933ec37d714bd4c2545da43add2.png        }d18c02628675d0a2c816449d98bda930.png

8f1ba5b45633e9678d1db480c16cae3f.png    }

然后在使用一个表达式来描述Foo里的各种属性:

4f1150b881333f12a311ae9ef34da474.pngFoo foo=newFoo(100,3.14f,newDate());

4f1150b881333f12a311ae9ef34da474.png        Mapenv=newHashMap();

4f1150b881333f12a311ae9ef34da474.png        env.put("foo", foo);

4f1150b881333f12a311ae9ef34da474.png

4f1150b881333f12a311ae9ef34da474.png        String result=4f1150b881333f12a311ae9ef34da474.png                (String) AviatorEvaluator.execute(

4f1150b881333f12a311ae9ef34da474.png"'[foo i='+ foo.i + ' f='+foo.f+' year='+(foo.date.year+1900)+ ' month='+foo.date.month +']'",

4f1150b881333f12a311ae9ef34da474.png                    env);

我们可以通过foo.date.year的方式来访问变量foo中date属性的year值,这是利用commons-beanutils的反射功能实现的,前提是你的变量是合法的JavaBean(public、getter缺一不可)。

三元表达式:

4f1150b881333f12a311ae9ef34da474.pngAviatorEvaluator.execute("3>0? 'yes':'no'");

上面都还是一个求值器表达式的常见功能,下面要描述的是Aviator的一些偏脚本性的功能。

类Ruby、Perl的正则匹配,匹配email地址:

4f1150b881333f12a311ae9ef34da474.pngAviatorEvaluator.execute("'killme2008'=~/([\\w0-8]+@\\w+[\\.\\w+]+)/");

成功的话返回true,否则返回false。//括起来的字符序列形成一个正则表达式Pattern类型,=~用于匹配,只能在String和Pattern之间使用。

匹配成功,获得匹配的分组,利用变量$digit:

4f1150b881333f12a311ae9ef34da474.pngAviatorEvaluator.execute("'killme2008@gmail.com'=~/([\\w0-8]+@\\w+[\\.\\w+]+)/ ? $1:'unknow'");

4f1150b881333f12a311ae9ef34da474.png

匹配成功返回$1,表示第一个匹配的分组,也就是用户名 killme2008

函数调用:

4f1150b881333f12a311ae9ef34da474.pngAviatorEvaluator.execute("sysdate()");

sysdate()是一个内置函数,返回当前日期,跟new java.util.Date()效果相同。

更多内置函数:

4f1150b881333f12a311ae9ef34da474.pngAviatorEvaluator.execute("string.length('hello')");//求字符串长度4f1150b881333f12a311ae9ef34da474.pngAviatorEvaluator.execute("string.contains('hello','h')");//判断字符串是否包含字符串4f1150b881333f12a311ae9ef34da474.pngAviatorEvaluator.execute("string.startsWith('hello','h')");//是否以子串开头4f1150b881333f12a311ae9ef34da474.pngAviatorEvaluator.execute("string.endsWith('hello','llo')");  是否以子串结尾

4f1150b881333f12a311ae9ef34da474.png

4f1150b881333f12a311ae9ef34da474.pngAviatorEvaluator.execute("math.pow(-3,2)");//求n次方4f1150b881333f12a311ae9ef34da474.pngAviatorEvaluator.execute("math.sqrt(14.0)");//开平方根4f1150b881333f12a311ae9ef34da474.pngAviatorEvaluator.execute("math.sin(20)");//正弦函数

4f1150b881333f12a311ae9ef34da474.png

可以看到Aviator的函数调用风格非常类似lua或者c。

自定义函数,实现AviatorFunction接口并注册即可,比如我们实现一个add函数用于相加:

4f1150b881333f12a311ae9ef34da474.pngimportcom.googlecode.aviator.AviatorEvaluator;

4f1150b881333f12a311ae9ef34da474.pngimportcom.googlecode.aviator.runtime.function.FunctionUtils;

4f1150b881333f12a311ae9ef34da474.pngimportcom.googlecode.aviator.runtime.type.AviatorDouble;

4f1150b881333f12a311ae9ef34da474.pngimportcom.googlecode.aviator.runtime.type.AviatorFunction;

4f1150b881333f12a311ae9ef34da474.pngimportcom.googlecode.aviator.runtime.type.AviatorObject;

1fa987a29c6482f53d401256f96355eb.png

ca75c07623e1b494fee67e8f316fc310.gifclassAddFunctionimplementsAviatorFunction9b8a8a44dd1c74ae49c20a7cd451974e.png{

d18c02628675d0a2c816449d98bda930.png

97e794c86028c5f5b5461ae5ef440a4c.png

3c6cafce68eb941a00f1998f1d3d3aa6.gifpublicAviatorObject call(Mapenv, AviatorObject

9b8a8a44dd1c74ae49c20a7cd451974e.png args)9b8a8a44dd1c74ae49c20a7cd451974e.png{

97e794c86028c5f5b5461ae5ef440a4c.png

3c6cafce68eb941a00f1998f1d3d3aa6.gifif(args.length!=2)9b8a8a44dd1c74ae49c20a7cd451974e.png{

d18c02628675d0a2c816449d98bda930.pngthrownewIllegalArgumentException("Add only supports two arguments");

ecedf933ec37d714bd4c2545da43add2.png            }d18c02628675d0a2c816449d98bda930.png            Number left=FunctionUtils.getNumberValue(0, args, env);

d18c02628675d0a2c816449d98bda930.png            Number right=FunctionUtils.getNumberValue(1, args, env);

d18c02628675d0a2c816449d98bda930.pngreturnnewAviatorDouble(left.doubleValue()+right.doubleValue());

ecedf933ec37d714bd4c2545da43add2.png        }d18c02628675d0a2c816449d98bda930.png

d18c02628675d0a2c816449d98bda930.png

97e794c86028c5f5b5461ae5ef440a4c.png

3c6cafce68eb941a00f1998f1d3d3aa6.gifpublicString getName()9b8a8a44dd1c74ae49c20a7cd451974e.png{

d18c02628675d0a2c816449d98bda930.pngreturn"add";

ecedf933ec37d714bd4c2545da43add2.png        }d18c02628675d0a2c816449d98bda930.png

8f1ba5b45633e9678d1db480c16cae3f.png    }4f1150b881333f12a311ae9ef34da474.png

注册并调用:

4f1150b881333f12a311ae9ef34da474.pngAviatorEvaluator.addFunction(newAddFunction());

4f1150b881333f12a311ae9ef34da474.png        System.out.println(AviatorEvaluator.execute("add(1,2)"));

4f1150b881333f12a311ae9ef34da474.png        System.out.println(AviatorEvaluator.execute("add(add(1,2),100)"));

函数可以嵌套调用。

三、不公平的性能测试

基本介绍完了,最后给些测试的数据,下列的测试场景都是每个表达式预先编译,然后执行1000万次,测量执行耗时。

场景1:

算术表达式   1000+100.0*99-(600-3*15)/(((68-9)-3)*2-100)+10000%7*71

结果:

测试

耗时(单位:秒)

Aviator

14.0

Groovy

79.6

IKExpression

159.2

场景2:

计算逻辑表达式和三元表达式混合: 6.7-100>39.6?5==5?4+5:6-1:!(100%3-39.0<27)?8*2-199:100%3

测试结果:

测试

耗时(单位:秒)

Aviator

11.0

Groovy

13.0

IKExpression

168.8

场景3:

计算算术表达式和逻辑表达式的混合,带有5个变量的表达式:

4f1150b881333f12a311ae9ef34da474.pngi*pi+(d*b-199)/(1-d*pi)-(2+100-i/pi)%99==i*pi+(d*b-199)/(1-d*pi)-(2+100-i/pi)%994f1150b881333f12a311ae9ef34da474.png

变量设定为:

4f1150b881333f12a311ae9ef34da474.pnginti=100;

4f1150b881333f12a311ae9ef34da474.pngfloatpi=3.14f;

4f1150b881333f12a311ae9ef34da474.pngdoubled=-3.9;

4f1150b881333f12a311ae9ef34da474.pngbyteb=(byte)4;

4f1150b881333f12a311ae9ef34da474.pngbooleanbool=false;

每次执行前都重新设置这些变量的值。

结果:

测试

耗时(单位:秒)

Aviator

31.2

Groovy

9.7

IKExpression

编译错误

场景4:

Aviator执行 sysdate()

groovy执行 new java.util.Date()

IKExpression执行 $SYSDATE()

结果:

测试

耗时(单位:秒)

Aviator

22.6

Groovy

13.9

IKExpression

25.4

原始的测试报告在这里。

四、结语

能看到这里,并且感兴趣的朋友请点击项目主页:

下载地址:

完整的用户手册:

目前版本仍然是1.0.0-RC,希望更多朋友试用并最终release。有什么疑问或者建议请跟贴。


http://chatgpt.dhexx.cn/article/tEvUu6EW.shtml

相关文章

Aviator

Aviator 简介 Aviator是一个高性能、轻量级的java语言实现的表达式求值引擎&#xff0c;主要用于各种表达式的动态求值。现在已经有很多开源可用的java表达式求值引擎&#xff0c;为什么还需要Avaitor呢&#xff1f; Aviator的设计目标是轻量级和高性能 &#xff0c;相比于Groo…

Aviator介绍

Aviator简介 Aviator是一个高性能、轻量级的 java 语言实现的表达式求值引擎, 主要用于各种表达式的动态求值。现在已经有很多开源可用的 java 表达式求值引擎,为什么还需要 Avaitor 呢? Aviator的设计目标是轻量级和高性能,相比于Groovy、JRuby的笨重, Aviator非常小, 加上…

西电计算机学院名誉院长,杨孟飞院士受聘为西电计科院名誉院长及讲席教授

西电新闻网讯(通讯员 陈龙)12月21日上午&#xff0c;西安电子科技大学计算机科学与技术学院名誉院长杨孟飞院士“华山学者”讲席教授及战略咨询委员会委员聘任仪式在北校区主楼Ⅱ区319会议室举行&#xff0c;校长杨宗凯、副校长王泉参加仪式。仪式由计科院执行院长崔江涛主持。…

北航计算机学院新闻,澳门理工学院代表团访问我校计算机学院

北航新闻网1月21日电(通讯员 盛浩)2019年1月16日&#xff0c;澳门理工学院副院长李雁莲教授、孙毓奇教授等一行三人到北航计算机学院就双方合作等事项进行交流。 座谈会由先进计算机应用技术教育部工程研究中心熊璋教授主持&#xff0c;计算机学院副院长胡春明副教授、中心李超…

【华人学者风采】陈晓峰 西安电子科技大学

【华人学者风采】陈晓峰&#xff0c;西安电子科技大学网络与信息安全学院副院长。研究方向包括公钥密码学、金融密码学、云计算安全、数据安全、区块链技术及应用、人工智能安全。曾获荣誉&#xff1a; 国家万人计划科技创新领军人才&#xff0c;教育部“青年长江学者” &#…

港科资讯 | 倪明选校长等出席江门“双碳”实验室揭牌暨项目签约仪式

12月7日&#xff0c;由香港科技大学&#xff08;广州&#xff09;&#xff08;筹&#xff09;和江门市政府发起共建的江门“双碳”实验室举行了揭牌暨项目签约仪式&#xff0c;江门双碳实验室正式启动成立。香港科技大学&#xff08;广州&#xff09;&#xff08;筹&#xff09…

莫队算法(普通莫队、带修莫队、树上莫队、不删除莫队)学习笔记【理解+套路/核心代码+例题及题解】

一、理解 我的理解就是巧妙的暴力&#xff0c;利用双指针以及分块思想&#xff0c;巧妙的移动双指针&#xff0c;时间复杂度可以达到O(NlogN)。 强推博客&#xff1a;写的又好又全。链接 二、套路 1、普通莫队 【1】核心代码 bool cmp(node a,node b){return belong[a.l]…

香港理工大学计算机系石杰明老师组招收全奖博士生、博士后

来源&#xff1a;AI求职 香港理工大学 香港理工大学位于中国香港特别行政区&#xff0c;QS 最新排名世界 66 位。计算机系&#xff08;Department of Computing&#xff09;USNews CS 排名 36&#xff0c;泰晤士 CS 排名 79。 石杰明博士课题组成员包括有 985/211 顶尖高校背景…

【调剂】华侨大学计算机学院计算机视觉与模式识别实验室钟必能课题组研究生招生...

点击文末的阅读原文或者公众号界面左下角的调剂信息或者公众号回复“调剂”是计算机/软件等专业的所有调剂信息集合&#xff0c;会一直更新的。 课题组主页&#xff1a;https://cst.hqu.edu.cn/info/1109/2001.htmLinkedin: https://www.linkedin.com/in/bineng-zhong-71a36674…

莫队算法思想

目录 莫队算法普通莫队方法&#xff1a;主要代码结构&#xff1a;例题&#xff1a;小B的询问例题&#xff1a;小Z的袜子奇偶化排序 带修改的莫队小结&#xff1a; 莫队算法 莫队算法是由前国家队莫涛提出的一种算法&#xff0c;主要应用在一类离线区间查询的问题中&#xff0c…

【华人学者风采】冯佳时 新加坡国立大学

【华人学者风采】冯佳时&#xff0c;新加坡国立大学ECE系助理教授。本科毕业于中国科学技术大学&#xff0c;硕士毕业于中国科学院自动化研究所&#xff0c;博士毕业于新加坡国立大学。研究兴趣包括大污染数据分析&#xff0c;在线和分布式鲁棒性学习及其在对象识别中的应用。 …

港科夜闻|央视网专访香港科大(广州)(筹)校长倪明选教授,谈香港科技大学在科研及知识转移方面成就...

关注并星标 每周阅读港科夜闻 建立新视野 开启新思维 1、央视网专访香港科大(广州)(筹)校长倪明选教授&#xff0c;谈香港科技大学在科研及知识转移方面成就。香港科技大学(广州)(筹)校长倪明选教授接受央视网专访&#xff0c;谈香港科技大学在科研及知识转移方面取得的成就&am…

独家对话许诗军:数字化转型,最基本的是不去拒绝 |数字价值观察室(下)...

关注ITValue&#xff0c;看企业级最新鲜、最价值报道&#xff01; ▎本文摘自《云栖战略参考》&#xff0c;这本刊物由阿里云与钛媒体联合策划。目的是为了把各个行业先行者的技术探索、业务实践呈现出来&#xff0c;与思考同样问题的“数字先行者”共同探讨、碰撞&#xff0c;…

港科夜闻|香港科技大学(广州)(筹)校长倪明选教授在北京拜访国家教育部党组书记、部长怀进鹏...

关注并星标 每周阅读港科夜闻 建立新视野 开启新思维 1、香港科技大学(广州)(筹)校长倪明选教授在北京拜访国家教育部党组书记、部长怀进鹏。2021年11月1日&#xff0c;香港科技大学(广州)(筹)校长倪明选教授等一行在北京拜访国家教育部党组书记、部长怀进鹏。 2、深圳先进院与…

特么的. 最终把 amobbs 的站长阿莫(莫进明)给回骂了一顿.

起因: 假设你居住的地方&#xff0c;要上马PX等高污染的项目&#xff0c;你会怎么做. 鼓动别人上街暴力示威与军警对抗. 自己待在家里支持怂恿. 这样的人真心猥琐! 鉴于他常常私自改动帖子, 在此截图留存. 真特么没劲. 居然以封锁别人 ID 作为别人"打不还手骂不还口…

中国计算机专业创始人,无怨无悔来时路――访计算机专业创始人吴忠明校友

哈工大报讯(研究生记者团杜&#xfffd;/文 李贵才/图)&#xff15;&#xff10;年前&#xff0c;他和他的同事创建了中国最早的计算机专业之一――哈工大计算机专业。从此&#xff0c;“哈工大计算机”作为哈工大的一张名片&#xff0c;&#xff15;&#xff10;载享誉海内外。…

史上首次,45岁计算机大牛蒋濛当选普渡大学校长!

作者丨编辑部 来源丨新智元 【导读】普渡大学史上首位华裔校长诞生&#xff01;45岁计算机大牛蒋濛当选&#xff0c;本硕博全藤校&#xff0c;产学研大满贯的他&#xff0c;此前曾屡拒其他名校。 刚刚&#xff0c;年仅45岁的华裔科学家蒋濛被任命为美国普渡大学第13任校长。 这…

港科夜闻|香港科技大学(广州)倪明选校长一行到访广东省科学技术厅,与龚国平厅长、吴世文副厅长参加交流座谈会议...

关注并星标 每周阅读港科夜闻 建立新视野 开启新思维 1、7月21日&#xff0c;香港科技大学&#xff08;广州&#xff09;倪明选校长一行到访广东省科学技术厅&#xff0c;与龚国平厅长、吴世文副厅长参加交流座谈会议。座谈会上&#xff0c;倪明选校长介绍了香港科大&#xff0…

apktool反编译apk教程

1.准备工具 (1)apktool的下载地址:https://bitbucket.org/iBotPeaches/apktool/downloads/ 点击超链接下载最新版本 (2)apktool.bat:将下面的脚本复制到文本并保存&#xff0c;然后重命名为apktool.bat; echo off setlocal set BASENAMEapktool_ chcp 65001 2>nul >nul…

C#反编译工具Reflector.exe教程

reflector.exe是一款专业的.NET反编译软件。reflector.exe可以分析程序集并向你展示它的所有秘密。.NET 框架向全世界引入了可用来分析任何基于 .NET 的代码(无论它是单个类还是完整的程序集)的反射概念。反射还可以用来检索有关特定程序集中包含的各种类、方法和属性的信息。 …