程序与技术分享:Aviator表达式求值引擎开源框架

简介: 程序与技术分享:Aviator表达式求值引擎开源框架

简介?


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的结构非常简单,一个典型的求值器的结构


依赖包?


commons-beanutils和commons-logging


使用手册?


执行表达式?


Aviator的使用都是集中通过com.googlecode.aviator.AviatorEvaluator这个入口类来处理,最简单的例子,执行一个计算1+2+3的表达式:


import com.googlecode.aviator.AviatorEvaluator;


public class SimpleExample {


public static void main(String【】 args) {


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


System.out.println(result);


}


}


细心的朋友肯定注意到结果是Long,而不是Integer。这是因为Aviator的数值类型仅支持Long和Double,任何整数都将转换成Long,任何浮点数都将转换为Double,包括用户传入的变量数值。这个例子的打印结果将是正确答案6。


使用变量?


想让Aviator对你say hello吗?很简单,传入你的名字,让Aviator负责字符串的相加:


import com.googlecode.aviator.AviatorEvaluator;


public class SayHello {


public static void main(String【】 args) {


if (args.length [span class="pln"> 1) {


System.err.print("Usesage: Java SayHello yourname");


}


String yourname = args【0】;


Map[span class="typ">String, ObjectString, Object

env.put("yourname", yourname);


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


System.out.println(result);


}


}


上面的例子演示了怎么向表达式传入变量值,表达式中的yourname是一个变量,默认为null,通过传入Map的变量绑定环境,将yourname设置为你输入的名称。env的key是变量名,//代码效果参考:http://www.jhylw.com.cn/441837179.html

value是变量的值。

上面例子中的'hello '是一个Aviator的String,Aviator的String是任何用单引号或者双引号括起来的字符序列,String可以比较大小(基于unicode顺序),可以参与正则匹配,可以与任何对象相加,任何对象与String相加结果为String。String中也可以有转义字符,如/n、//、/'等。


AviatorEvaluator.execute(" 'a/"b' "); //字符串 a'b


AviatorEvaluator.execute(" /"a/'b/" "); //字符串 a"b


AviatorEvaluator.execute(" 'hello'+3 "); //字符串 hello 3


AviatorEvaluator.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即可使用


public interface AviatorFunction {


/


Get the function name



@return


/


public String getName();


/


call function



@param env


Variable environment


@param args


Function arguments


@return


/


public AviatorObject call(Map[span class="typ">String, Object

}


可以看一个例子,我们实现一个add函数来做数值的相加:


import com.googlecode.aviator.runtime.function.FunctionUtils;


import com.googlecode.aviator.runtime.type.AviatorDouble;


import com.googlecode.aviator.runtime.type.AviatorFunction;


import com.googlecode.aviator.runtime.type.AviatorObject;


public class AddFunction implements AviatorFunction {


public AviatorObject call(Map[span class="typ">String, Object

if (args.length != 2) {


throw new IllegalArgumentException("Add only supports two arguments");


}


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


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


return new AviatorDouble(left.doubleValue() + right.doubleValue());


}


public String getName() {


return "add";


}


}


注册到AviatorEvaluator并调用如下:


//注册函数


AviatorEvaluator.addFunction(new AddFunction());


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


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


注册函数通过AviatorEvaluator.addFunction方法,移除可以通过removeFunction。


编译表达式?


上面提到的例子都是直接执行表达式,事实上Aviator背后都帮你做了编译并执行的工作。你可以自己先编译表达式,返回一个编译的结果,然后传入不同的env来复用编译结果,提高性能,这是更推荐的使用方式:


import java.util.HashMap;


import java.util.Map;


import com.googlecode.aviator.AviatorEvaluator;


import com.googlecode.aviator.Expression;


public class CompileExample {


public static void main(String【】 args) {


String expression = "a-(b-c)>100";


// 编译表达式


Expression compiledExp = AviatorEvaluator.compile(expression);


Map[span class="typ">String, ObjectString, Object

env.put("a", 100.3);


env.put("b", 45);


env.put("c", -199.100);


// 执行表达式


Boolean result = (Boolean) compiledExp.execute(env);


System.out.println(result);


}


}


通过compile方法可以将表达式编译成Expression的中间对象,当要执行表达式的时候传入env并调用Expression的execute方法即可。表达式中使用了括号来强制优先级,这个例子还使用了>用于比较数值大小,比较运算符!=、==、>、>=、<、<=不仅可以用于数值,也可以用于String、Pattern、Boolean等等,甚至是任何用户传入的两个都实现了java.lang。Comparable接口的对象之间。


编译后的结果你可以自己缓存,也可以交给Aviator帮你缓存,AviatorEvaluator内部有一个全局的缓存池,如果你决定缓存编译结果,可以通过:


public static Expression compile(String expression, boolean cached)


将cached设置为true即可,那么下次编译同一个表达式的时候将直接返回上一次编译的结果。使缓存失效通过:


public static void invalidateCache(String expression)


方法。


访问数组和集合?


可以通过中括号去访问数组和java.util.List对象,可以通过map.key访问java.util.Map中key对应的value,一个例子:


import java.util.ArrayList;


import java.util.Date;


import java.util.HashMap;


import java.util.List;


import java.util.Map;


import com.googlecode.aviator.AviatorEvaluator;


public class CollectionExample {


public static void main(String【】 args) {


final List[span class="typ">StringString

list.add("hello");


list.add(" world");


final int【】 array = new int【3】;


array【0】 = 0;


array【1】 = 1;


array【2】 = 3;


final Map[span class="typ">String, DateString, Date

map.put("date", new Date());


&

相关文章
|
7月前
|
缓存 安全 Java
|
7月前
|
Java
探索Java世界的奇妙工具——运算符与表达式运算符
探索Java世界的奇妙工具——运算符与表达式运算符
34 0
八股day02_程序流程控制和数据类型
八股day02_程序流程控制和数据类型
|
7月前
|
C++
【C/C++ 编程 入门篇】 if条件判断表达式的使用教程
https://developer.aliyun.com/article/1465430
141 0
|
7月前
|
存储 算法 安全
【C++ 泛型编程 高级篇】 C++编译时函数调用技术深度解析
【C++ 泛型编程 高级篇】 C++编译时函数调用技术深度解析
89 1
|
7月前
|
存储 程序员 C语言
“探索C语言操作符的神秘世界:从入门到精通的全方位解析“
“探索C语言操作符的神秘世界:从入门到精通的全方位解析“
|
7月前
|
C++
关系表达式:编程中的比较利器
在编程中,关系表达式扮演着至关重要的角色。它们允许我们比较两个或多个值,并基于这些比较的结果来执行相应的操作。关系表达式通过返回布尔值(真或假)来告诉我们两个值之间的关系,从而帮助我们在程序中做出决策。
70 0
|
7月前
|
SQL Arthas Java
OGNL表达式学习笔记(一) 基本特性与基本概念
OGNL表达式学习笔记(一) 基本特性与基本概念
|
7月前
|
JavaScript
连等表达式的核心原理
连等表达式的核心原理
|
Rust
Rust 基础入门 —— 语句与表达式
语句与表达式 这一节,我们接触的是rust中的有一个基本类型 我将其称之为 —— 逻辑结构,这个是我自己命名的,但我觉得很贴切。
63 3