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

一、轮子的必要性

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

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

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

二、让轮子转起来。

求算术表达式:

importcom.googlecode.aviator.AviatorEvaluator;

publicclassSimpleExample{

publicstaticvoidmain(String[] args){

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

        System.out.println(result);

    }}

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

逻辑表达式和关系运算:

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

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

使用变量和字符串相加:

String yourname=“aviator”;

        Mapenv=newHashMap();

        env.put("yourname", yourname);

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

        System.out.println(result);

打印:

hello aviator

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

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

publicstaticclassFoo{

inti;

floatf;

        Date date=newDate();

publicFoo(inti,floatf, Date date){

super();

this.i=i;

this.f=f;

this.date=date;

        }

publicintgetI(){

returni;

        }

publicvoidsetI(inti){

this.i=i;

        }

publicfloatgetF(){

returnf;

        }

publicvoidsetF(floatf){

this.f=f;

        }

publicDate getDate(){

returndate;

        }

publicvoidsetDate(Date date){

this.date=date;

        }

    }

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

Foo foo=newFoo(100,3.14f,newDate());

        Mapenv=newHashMap();

        env.put("foo", foo);

        String result=                (String) AviatorEvaluator.execute(

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

                    env);

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

三元表达式:

AviatorEvaluator.execute("3>0? 'yes':'no'");

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

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

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

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

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

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

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

函数调用:

AviatorEvaluator.execute("sysdate()");

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

更多内置函数:

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

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

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

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

importcom.googlecode.aviator.AviatorEvaluator;

importcom.googlecode.aviator.runtime.function.FunctionUtils;

importcom.googlecode.aviator.runtime.type.AviatorDouble;

importcom.googlecode.aviator.runtime.type.AviatorFunction;

importcom.googlecode.aviator.runtime.type.AviatorObject;

classAddFunctionimplementsAviatorFunction{

publicAviatorObject call(Mapenv, AviatorObject

 args){

if(args.length!=2){

thrownewIllegalArgumentException("Add only supports two arguments");

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

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

returnnewAviatorDouble(left.doubleValue()+right.doubleValue());

        }

publicString getName(){

return"add";

        }

    }

注册并调用:

AviatorEvaluator.addFunction(newAddFunction());

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

        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个变量的表达式:

i*pi+(d*b-199)/(1-d*pi)-(2+100-i/pi)%99==i*pi+(d*b-199)/(1-d*pi)-(2+100-i/pi)%99

变量设定为:

inti=100;

floatpi=3.14f;

doubled=-3.9;

byteb=(byte)4;

booleanbool=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。有什么疑问或者建议请跟贴。

java aviator_Aviator——让表达式飞起来相关推荐

  1. Java 8 Lambda 表达式被编译成了什么?

    点击上方"朱小厮的博客",选择"设为星标" 后台回复"书",获取个gui 在了解了Java 8 Lambda的一些基本概念和应用后, 我们会 ...

  2. 10个Java 8 Lambda表达式经典示例

    Java 8 刚于几周前发布,日期是2014年3月18日,这次开创性的发布在Java社区引发了不少讨论,并让大家感到激动.特性之一便是随同发布的lambda表 达式,它将允许我们将行为传到函数里.在J ...

  3. BeanShell中的Java指令和表达式

    我的博客原址:点击此处 BeanShell 能够理解标准的 Java 指令,表达式和方法的语法.那些指令和表达式都是在一个 Java 方法中你会提到的非常常见的东西,像变量的定义和分配,方法调用,循环 ...

  4. Java 8 Lambda 表达式详解

    版权声明:本文由吴仙杰创作整理,转载请注明出处:https://segmentfault.com/a/1190000009186509 1. 引言 在 Java 8 以前,若我们想要把某些功能传递给某 ...

  5. Java 8 Lambda表达式-接口实现

    Java 8 Lambda表达式在只有一个方法的接口实现代码编写中,可以起到简化作用: (argument list) -> body 具体看Runnable接口的例子 public class ...

  6. 这是一个有趣的问题,Java 8 Lambda 表达式被编译成了什么?

    在了解了Java 8 Lambda的一些基本概念和应用后, 我们会有这样的一个问题: Lambda表达式被编译成了什么? 这是一个有趣的问题,涉及到JDK的具体的实现.本文将介绍OpenJDK对Lam ...

  7. java 8 lambda表达式中的异常处理

    文章目录 简介 处理Unchecked Exception 处理checked Exception 总结 java 8 lambda表达式中的异常处理 简介 java 8中引入了lambda表达式,l ...

  8. java 正则表达式 开头_如何在Java中修复表达式的非法开头

    java 正则表达式 开头 您是否遇到过这个令人难以置信的错误,想知道如何解决它? 让我们仔细阅读一下,研究如何解决表达式Java非法开头错误. 这是一个动态错误,这意味着编译器会发现某些不符合Jav ...

  9. 如何在Java中修复表达式的非法开头

    您是否遇到过这个令人难以置信的错误,想知道如何解决它? 让我们浏览一下这篇文章,研究如何解决表达式Java非法开头错误. 这是一个动态错误,这意味着编译器会发现某些不符合Java编程规则或语法的内容. ...

最新文章

  1. shell脚本编程第一天
  2. 框架:spring、springmvc、springboot
  3. asp.Net_图片上传的一个类库的源码
  4. vue-router.esm.js?fe87:16 [vue-router] Route with name 'page' does not exist
  5. 毕业典礼校长致辞金句频出:搬砖也要元气满满
  6. JAVA String类特点
  7. 论文笔记_S2D.61_2019-CVPR-DeepLiDAR:基于稀疏激光雷达数据和单张彩色图像的户外场景的表面法线引导的深度预测
  8. 网上银行说此服务器证书无效,个人网银业务操作时,系统提示“客户端证书无效”,该如何解决?...
  9. android手机如何查看系统版本号,怎么查看安卓系统版本
  10. setw和width
  11. 将淘宝客链接转换为正常淘宝、天猫商品链接
  12. 富途客户端软件测试笔试and面试
  13. 计算机图解教程视频教程,新手怎么制作短视频教程?视频处理的图文步骤
  14. Idea设置代码自动提示快捷键
  15. IO(BIO),NIO,AIO的深度解析和区别
  16. Android日历移植小结
  17. 图像处理与分析--国内公司就业
  18. 浅述单目3D目标检测
  19. Linux 之父是个果粉:给自用 M2 Mac 装上 Fedora Linux,除图形加速和 Chrome 都搞定了...
  20. SCRM营销: 关于微信个人号批量运营的干货分享!

热门文章

  1. python-自动化Airtest-3IDE poco介绍
  2. 爱彼迎数据采集与预处理-图片色彩分析
  3. nginx代理rabbitmq
  4. Jenkins 详细介绍
  5. utu2440 gdbserver 搭建
  6. c语言代码 linux 关机_linux下c语言关机程序
  7. 彩色图像的直方图均衡化
  8. 等价关系和偏序关系【】
  9. PTA 一 愿天下有情人都是失散多年的兄妹
  10. Linux查看GPU温度和占用