Fel表达式引擎
1、名词解释
EL:表达式语言,用于求表达式的值。
Ast:抽象语法树,一般由语法分析工具生成。1+2*3会解析结果如下所示:
2、简介
Fel(Fast Expression Language)在源自于企业项目,设计目标是为了满足不断变化的功能需求和性能需求。
Fel是开放的,引擎执行中的多个模块都可以扩展或替换。Fel的执行主要是通过函数实现,运算符(+、-等都是Fel函数),所有这些函数都是可以替换的,扩展函数也非常简单。
Fel有双引擎,同时支持解释执行和编译执行。可以根据性能要求选择执行方式。编译执行就是将表达式编译成字节码(生成java代码和编译模块都是可以扩展和替换的)
Fel基于Java1.5开发,适用于Java1.5及以上版本。
1. 特点
易用性:API使用简单,语法简洁,和java语法很相似。
轻量级:整个包只有300多KB。
高 效:目前没有发现有开源的表达式引擎比Fel快。
扩展性:采用模块化设计,可灵活控制表达式的执行过程。
根函数:Fel支持根函数,“$('Math')”在Fel中是常用的使用函数的方式。
$函数:通过$函数,Fel可以方便的调用工具类或对象的
方法
快递客服问题件处理详细方法山木方法pdf计算方法pdf华与华方法下载八字理论方法下载
(并不需要任何附加代码),
2. 不足
支持脚本:否。
支持数组:否。Fel不支持类似于array[i]这样的语法,可以通过其他方法获取数据值。后续版本可以改进。
3. 适应场景
Fel适合处理海量数据,Fel良好的扩展性可以更好的帮助用户处理数据。
Fel同样适用于其他需要使用表达式引擎的地方(如果工作流、公式计算、数据有效性校验等等)
3、功能
4. 算术表达式:
FelEngine fel= new FelEngineImpl();
Object result= fel.eval("5000*12+7500");
System.out.println(result);
输出结果:67500
5. 变量
使用变量,其代码如下所示:
FelContext ctx= fel.getContext();
ctx.set("单价", 5000);
ctx.set("数量", 12);
ctx.set("运费", 7500);
Object result= fel.eval("单价*数量+运费");
System.out.println(result);
输出结果:67500
6. 调用JAVA方法
FelEngine fel= new FelEngineImpl();
FelContext ctx= fel.getContext();
ctx.set("out", System.out);
fel.eval("out.println('Hello Everybody'.substring(6))");
输出结果:Everybody
7. 自定义上下文环境
//负责提供气象服务的上下文环境
FelContext ctx= new AbstractConetxt() {
public Object get(Object name) {
if("天气".equals(name)){
return "晴";
}
if("温度".equals(name)){
return 25;
}
return null;
}
};
FelEngine fel= new FelEngineImpl(ctx);
Object eval = fel.eval("'天气:'+天气+';温度:'+温度");
System.out.println(eval);
输出结果:天气:晴;温度:25
8. 多层上下文环境(命名空间)
FelEngine fel= new FelEngineImpl();
String costStr= "成本";
String priceStr="价格";
FelContext baseCtx= fel.getContext();
//父级上下文中设置成本和价格
baseCtx.set(costStr, 50);
baseCtx.set(priceStr,100);
String exp= priceStr+"-"+costStr;
Object baseCost= fel.eval(exp);
System.out.println("期望利润:" + baseCost);
FelContext ctx= new ContextChain(baseCtx, new MapContext());
//通货膨胀导致成本增加(子级上下文 中设置成本,会覆盖父级上下文中的成本)
ctx.set(costStr,50+20 );
Object allCost= fel.eval(exp, ctx);
System.out.println("实际利润:" + allCost);
输出结果:
期望利润:50
实际利润:30
9. 编译执行
FelEngine fel= new FelEngineImpl();
FelContext ctx= fel.getContext();
ctx.set("单价", 5000);
ctx.set("数量", 12);
ctx.set("运费", 7500);
Expression exp= fel.compile("单价*数量+运费",ctx);
Object result= exp.eval(ctx);
System.out.println(result);
执行结果:67500
备注:适合处理海量数据,编译执行的速度基本与Java字节码执行速度一样快。
10. 自定义函数
//定义hello函数
Function fun= new CommonFunction() {
public String getName() {
return "hello";
}
/*
* 调用hello("xxx")时执行的代码
*/
@Override
public Object call(Object[] arguments) {
Object msg= null;
if(arguments!= null && arguments.length>0){
msg= arguments[0];
}
return ObjectUtils.toString(msg);
}
};
FelEngine e= new FelEngineImpl();
//添加函数到引擎中。
e.addFun(fun);
String exp= "hello('fel')";
//解释执行
Object eval = e.eval(exp);
System.out.println("hello "+eval);
//编译执行
Expression compile= e.compile(exp, null);
eval = compile.eval(null);
System.out.println("hello "+eval);
执行结果:
hello fel hello fel
11. 调用静态方法 (始于0.5版本)
如果你觉得上面的自定义函数也麻烦,Fel提供的$函数可以方便的调用工具类的方法 熟悉jQuery的朋友肯定知道"$"函数的威力。Fel东施效颦,也实现了一个"$"函数,其作用是获取class和创建对象。结合点操作符,可以轻易的调用工具类或对象的方法。
//调用Math.min(1,2)
FelEngine.instance.eval("$('Math').min(1,2)");
//调用new Foo().toString();
FelEngine.instance.eval("$('com.greenpineyu.test.Foo.new').toString());
通过"$('class').method"形式的语法,就可以调用任何等三方类包(commons lang等)及自定义工具类的方法,也可以创建对象,调用对象的方法。 未来版本的Fel会考虑直接注册java method。
4、语法
12. 简介
Fel的语法非常简单,基本和Java语法没没有区别。Fel语法是Java语法的一个子集,支持的运算符有限。如果熟悉语法的话,只需要关注一下Fel支持的运算即可。
Fel表达式由常量、变量、函数、运算符组成。详见下文。
13. 常量
常量
说明
Number
1,1.0等
Boolean
true,false
String
'abc',"abc"
14. 变量
变量命名规则与java变量相同。变量名由字母、数字、下划线、$组成。
变量
说明
abc
以字母开头
abc
以下划线开头
$abc
以$开头
变量
中文变量名也可以
15. 函数
函数名的语法规则与变量相同
函数
说明
$('Math')
$是变量名,'Math'是参数
16. 运算符
运算符
类型
优先级
说明
?:
条件
优先级从低到高
三元操作符
||
逻辑
或操作
&&
与操作
==、!=
关系
等于、不等于
>、<、<=、>=
大于、小于、大于等于、小于等于
+、-
算术
加减
*、/、%
乘、除、取模
!
逻辑
取反操作
5、结构
17. 主要组成部分
引擎由四部分组成,每个组成部分都可以被替换。组成部分如下所示:
组件
说明
解析器
将表达式解析成Ast节点
解释执行
负责执行节点
执行上下文
负责提供变量
编译器
负责生成代码并编译
18. 主要
流程
快递问题件怎么处理流程河南自建厂房流程下载关于规范招聘需求审批流程制作流程表下载邮件下载流程设计
执行表达式的流程如下所示:
组件
说明
解析表达式
默认Antlr解析表达式,生成Ast节点。
Ast节点
由解析器生成的抽象语法树节点
节点解释器
每一个节点都有一个解释器,负责解释节点。
编译器
负责生成表达式对应Java类并编译,生成Expression对象
19. 引擎上下文
引擎上下文负责存取变量,它是脚本引擎与Java对象之间的桥梁。其结构如下所示:
组件
说明
FelContext
负责存储和保存变量
MapContext
使用Map来保存变量
ContextChain
上下文链,保存两个上下文的引用。存取变量委托引用的上下文处理。
20. 表达式解析
解析表达式的过程就是将表达式解析成Ast节点的过程,目前Fel由Antlr负责解析表达式,生成Ast节点(FelNode),流程如下所示:
21. 解释执行
FelNode(Ast节点)中都包含一个解释器,不同的节点有不同的解释器,解释器负责求节点值,流程如下所示:
22. 编译执行
FelNode(Ast节点)中都包含一个代码生成器,负责将Fel表达式转换成java表达式。编译模块负责将java表达式封装成Java类、编译、创建Expression。再通过调用Expression.eval求表达式的值。其过程如下所示:
23. 小结
在上文介绍的Fel组件中,绝大多数组件都是可能被替换的。通过在运行时替换一些小粒度的组件,就可以灵活控制Fel的执行过程,很有实用价值。有些变化较少的组件(Antlr解析器)的替换过程还不是挺方便,随着Fel的发展,会慢慢重构,努力使Fel转变成让人满意的模块化结构。