简介¶
Aviator是一个高性能、轻量级的java语言实现的表达式求值引擎,主要用于各种表达式的动态求值。现在已经有很多开源可用的java表达式求值引擎,为什么还需要Avaitor呢?
Aviator的设计目标是轻量级和高性能,相比于Groovy、JRuby的笨重,Aviator非常小,加上依赖包也才450K,不算依赖包的话只有70K;当然,Aviator的语法是受限的,它不是一门完整的语言,而只是语言的一小部分集合。
其次,Aviator的实现思路与其他轻量级的求值器很不相同,其他求值器一般都是通过解释的方式运行,而Aviator则是直接将表达式编译成Java字节码,交给JVM去执行。简单来说,Aviator的定位是介于Groovy这样的重量级脚本语言和IKExpression这样的轻量级表达式引擎之间。
特性¶
Aviator的特性
支持大部分运算操作符,包括算术操作符、关系运算符、逻辑操作符、正则匹配操作符(=~)、三元表达式?: ,并且支持操作符的优先级和括号强制优先级,具体请看后面的操作符列表。
支持函数调用和自定义函数
支持正则表达式匹配,类似Ruby、Perl的匹配语法,并且支持类Ruby的$digit指向匹配分组。
自动类型转换,当执行操作的时候,会自动判断操作数类型并做相应转换,无法转换即抛异常。
支持传入变量,支持类似a.b.c的嵌套变量访问。
性能优秀
Aviator的限制:
没有if else、do while等语句,没有赋值语句,仅支持逻辑表达式、算术表达式、三元表达式和正则匹配。
没有位运算符
整体结构¶
Aviator的结构非常简单,一个典型的求值器的结构
依赖包¶
使用手册¶
执行表达式¶
Aviator的使用都是集中通过com.googlecode.aviator.AviatorEvaluator这个入口类来处理,最简单的例子,执行一个计算1+2+3的表达式:
importcom.googlecode.aviator.AviatorEvaluator;publicclassSimpleExample{publicstaticvoidmain(String[]args){Longresult=(Long)AviatorEvaluator.execute("1+2+3");System.out.println(result);}}
细心的朋友肯定注意到结果是Long,而不是Integer。这是因为Aviator的数值类型仅支持Long和Double,任何整数都将转换成Long,任何浮点数都将转换为Double,包括用户传入的变量数值。这个例子的打印结果将是正确答案6。
使用变量¶
想让Aviator对你say hello吗?很简单,传入你的名字,让Aviator负责字符串的相加:
importcom.googlecode.aviator.AviatorEvaluator;publicclassSayHello{publicstaticvoidmain(String[]args){if(args.length<1){System.err.print("Usesage: Java SayHello yourname");}Stringyourname=args[0];Mapenv=newHashMap();env.put("yourname",yourname);Stringresult=(String)AviatorEvaluator.execute(" 'hello ' + yourname ",env);System.out.println(result);}}
上面的例子演示了怎么向表达式传入变量值,表达式中的yourname是一个变量,默认为null,通过传入Map的变量绑定环境,将yourname设置为你输入的名称。env的key是变量名,value是变量的值。
上面例子中的'hello '是一个Aviator的String,Aviator的String是任何用单引号或者双引号括起来的字符序列,String可以比较大小(基于unicode顺序),可以参与正则匹配,可以与任何对象相加,任何对象与String相加结果为String。String中也可以有转义字符,如/n、//、/'等。
AviatorEvaluator.execute(" 'a/"b' ");//字符串 a'bAviatorEvaluator.execute(" /"a/'b/" ");//字符串 a"bAviatorEvaluator.execute(" 'hello'+3 ");//字符串 hello 3AviatorEvaluator.execute(" 'hello '+ unknow ");//字符串 hello null
调用函数¶
Aviator支持函数调用,函数调用的风格类似lua,下面的例子获取字符串的长度:
AviatorEvaluator.execute("string.length('hello')");
string.length('hello')是一个函数调用,string.length是一个函数,'hello'是调用的参数。
再用string.substring来截取字符串:
AviatorEvaluator.execute("string.contains(/"test/",string.substring('hello',1,2))");
通过string.substring('hello',1,2)获取字符串'e',然后通过函数string.contains判断e是否在'test'中。可以看到,函数可以嵌套调用。
Aviator的内置函数列表请看后面。
自定义函数¶
Aviator除了内置的函数之外,还允许用户自定义函数,只要实现com.googlecode.aviator.runtime.type.AviatorFunction接口,并注册到AviatorEvaluator即可使用
publicinterfaceAviatorFunction{/**
* Get the function name
*
* @return
*/publicStringgetName();/**
* call function
*
* @param env
* Variable environment
* @param args
* Function arguments
* @return
*/publicAviatorObjectcall(Mapenv,AviatorObject...args);}
可以看一个例子,我们实现一个add函数来做数值的相加:
importcom.googlecode.aviator.runtime.function.FunctionUtils;importcom.googlecode.aviator.runtime.type.AviatorDouble;importcom.googlecode.aviator.runtime.type.AviatorFunction;importcom.googlecode.aviator.runtime.type.AviatorObject;publicclassAddFunctionimplementsAviatorFunction{publicAviatorObjectcall(Mapenv,AviatorObject...args){if(args.length!=2){thrownewIllegalArgumentException("Add only supports two arguments");}Numberleft=FunctionUtils.getNumberValue(0,args,env);Numberright=FunctionUtils.getNumberValue(1,args,env);returnnewAviatorDouble(left.doubleValue()+right.doubleValue());}publicStringgetName(){return"add";}}
注册到AviatorEvaluator并调用如下:
//注册函数AviatorEvaluator.addFunction(newAddFunction());System.out.println(AviatorEvaluator.execute("add(1,2)"));System.out.println(AviatorEvaluator.execute("add(add(1,2),100)"));
注册函数通过AviatorEvaluator.addFunction方法,移除可以通过removeFunction。
编译表达式¶
上面提到的例子都是直接执行表达式,事实上Aviator背后都帮你做了编译并执行的工作。你可以自己先编译表达式,返回一个编译的结果,然后传入不同的env来复用编译结果,提高性能,这是更推荐的使用方式:
importjava.util.HashMap;importjava.util.Map;importcom.googlecode.aviator.AviatorEvaluator;importcom.googlecode.aviator.Expression;publicclassCompileExample{publicstaticvoidmain(String[]args){Stringexpression="a-(b-c)>100";// 编译表达式ExpressioncompiledExp=AviatorEvaluator.compile(expression);Mapenv=newHashMap();env.put("a",100.3);env.put("b",45);env.put("c",-199.100);// 执行表达式Booleanresult=(Boolean)compiledExp.execute(env);System.out.println(result);}}
通过compile方法可以将表达式编译成Expression的中间对象,当要执行表达式的时候传入env并调用Expression的execute方法即可。表达式中使用了括号来强制优先级,这个例子还使用了>用于比较数值大小,比较运算符!=、==、>、>=、
编译后的结果你可以自己缓存,也可以交给Aviator帮你缓存,AviatorEvaluator内部有一个全局的缓存池,如果你决定缓存编译结果,可以通过:
publicstaticExpressioncompile(Stringexpression,booleancached)
将cached设置为true即可,那么下次编译同一个表达式的时候将直接返回上一次编译的结果。使缓存失效通过:
publicstaticvoidinvalidateCache(Stringexpression)
方法。
访问数组和集合¶
可以通过中括号去访问数组和java.util.List对象,可以通过map.key访问java.util.Map中key对应的value,一个例子:
importjava.util.ArrayList;importjava.util.Date;importjava.util.HashMap;importjava.util.List;importjava.util.Map;importcom.googlecode.aviator.AviatorEvaluator;publicclassCollectionExample{publicstaticvoidmain(String[]args){finalListlist=newArrayList();list.add("hello");list.add(" world");finalint[]array=newint[3];array[0]=0;array[1]=1;array[2]=3;finalMapmap=newHashMap();map.put("date",newDate());Mapenv=newHashMap();env.put("list",list);env.put("array",array);env.put("mmap",map);System.out.println(AviatorEvaluator.execute("list[0]+list[1]+'/narray[0]+array[1]+array[2]='+(array[0]+array[1]+array[2]) +' /ntoday is '+mmap.date ",env));}}
三元操作符¶
Aviator不提供if else语句,但是提供了三元操作符?:用于条件判断,使用上与java没有什么不同:
importjava.util.HashMap;importjava.util.Map;importcom.googlecode.aviator.AviatorEvaluator;publicclassTernaryOperatorExample{publicstaticvoidmain(String[]args){if(args.length<1){System.err.println("Usage: java TernaryOperatorExample [number]");System.exit(1);}intnum=Integer.parseInt(args[0]);Mapenv=newHashMap();env.put("a",num);Stringresult=(String)AviatorEvaluator.execute("a>0? 'yes':'no'",env);System.out.println(