开源好用的表达式计算语言FEL,可惜了官网文档不在国内,我来copy个过来。

Fel是轻量级的高效的表达式计算引擎

Fel在源自于企业项目,设计目标是为了满足不断变化的功能需求和性能需求。

Fel是开放的,引擎执行中的多个模块都可以扩展或替换。Fel的执行主要是通过函数实现,运算符(+、-等都是Fel函数),所有这些函数都是可以替换的,扩展函数也非常简单。

Fel有双引擎,同时支持解释执行和编译执行。可以根据性能要求选择执行方式。编译执行就是将表达式编译成字节码(生成java代码和编译模块都是可以扩展和替换的)

Fel基于Java1.5开发,适用于Java1.5及以上版本。

表达式引擎QQ群:75055831,欢迎交流。

Fel有多快?

通常情况下,Fel-0.7每秒可以执行千万次表达式(不包含编译时间)。速度是Jexl-2.0的20倍以上。

目前还没有发现开源的表达式引擎比Fel快。

具体的测试数据请参见http://code.google.com/p/fast-el/wiki/Performance。

为何要使用Fel?

Fel语法和API非常简单,语法与Java基本相同,几乎没有学习成本。

Fel非常快,上面已经做了简单说明。

Fel整个包只有200多KB。

Fel可以非常方便的访问数组、集合、Map的元素和对象的属性。

Fel可以非常方便的调用对象的方法和类方法(如果这些还不够,可以添加自定义函数)。

Fel支持大数值高精度计算

Fel有良好的安全管理功能

如果Fel不能满足你的要求,扩展和修改Fel很简单。

Fel不能做什么?

Fel只支持表达式,不支持脚本。

Fel适用场景:

Fel适合处理海量数据,Fel良好的扩展性可以更好的帮助用户处理数据。

Fel同样适用于其他需要使用表达式引擎的地方(如果工作流、公式计算、数据有效性校验等等)

安装

1:获取Fel

项目主页:http://code.google.com/p/fast-el/

下载地址:http://code.google.com/p/fast-el/downloads/list

2:Jdk1.6环境

使用:将fel.jar加入classpath即可。 构建Fel:下载fel-all.tar.gz,解压后将src作为源码文件夹,并且将lib/antlr-min.jar加入classpath即可。

3:Jdk1.5环境:

与jdk1.6环境下的区别在于,需要添加jdk内置的tools.jar到classpath。

Fel使用例子:

1:算术表达式:
FelEngine fel = new FelEngineImpl();
Object result = fel.eval("5000*12+7500");
System.out.println(result); 输出结果:67500

2:变量

使用变量,其代码如下所示: FelContext ctx = fel.getContext();
ctx.set("单价", 5000);
ctx.set("数量", 12);
ctx.set("运费", 7500);
Object result = fel.eval("单价*数量+运费");
System.out.println(result);
输出结果:67500

3:访问对象属性

在Fel中,可能非常方便的访问对象属性,示例代码如下所示

FelEngine fel = new FelEngineImpl();
FelContext ctx = fel.getContext();
Foo foo = new Foo();
ctx.set("foo", foo);
Map m = new HashMap(); m.put("ElName", "fel"); ctx.set("m",m); //调用foo.getSize()方法。
Object result = fel.eval("foo.size"); //调用foo.isSample()方法。
result = fel.eval("foo.sample"); //foo没有name、getName、isName方法
//foo.name会调用foo.get("name")方法。
result = fel.eval("foo.name"); //m.ElName会调用m.get("ElName");
result = fel.eval("m.ElName");

4:访问数组、集合、Map

FelEngine fel = new FelEngineImpl();
FelContext ctx = fel.getContext(); //数组
int[] intArray = {1,2,3};
ctx.set("intArray",intArray);
//获取intArray[0]
String exp = "intArray[0]";
System.out.println(exp+"->"+fel.eval(exp)); //List
List list = Arrays.asList(1,2,3);
ctx.set("list",list);
//获取list.get(0)
exp = "list[0]";
System.out.println(exp+"->"+fel.eval(exp)); //集合
Collection coll = Arrays.asList("a","b","c");
ctx.set("coll",coll);
//获取集合最前面的元素。执行结果为"a"
exp = "coll[0]";
System.out.println(exp+"->"+fel.eval(exp)); //迭代器
Iterator iterator = coll.iterator();
ctx.set("iterator", iterator);
//获取迭代器最前面的元素。执行结果为"a"
exp = "iterator[0]";
System.out.println(exp+"->"+fel.eval(exp)); //Map
Map m = new HashMap();
m.put("name", "HashMap");
ctx.set("map",m); exp = "map.name";
System.out.println(exp+"->"+fel.eval(exp)); //多维数组
int[][] intArrays= {{11,12},{21,22}};
ctx.set("intArrays",intArrays);
exp = "intArrays[0][0]";
System.out.println(exp+"->"+fel.eval(exp)); //多维综合体,支持数组、集合的任意组合。
List listArray = new ArrayList();
listArray.add(new int[]{1,2,3});
listArray.add(new int[]{4,5,6});
ctx.set("listArray",listArray);
exp = "listArray[0][0]";
System.out.println(exp+"->"+fel.eval(exp));

5:调用JAVA方法

FelEngine fel = new FelEngineImpl();
FelContext ctx = fel.getContext();
ctx.set("out", System.out);
fel.eval("out.println('Hello Everybody'.substring(6))");
输出结果:Everybody

6:自定义上下文环境

//负责提供气象服务的上下文环境
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

7:多层上下文环境(命名空间)

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

8:编译执行

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字节码执行速度一样快。

9:自定义函数

//定义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

10:调用静态方法

如果你觉得上面的自定义函数也麻烦,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等)及自定义工具类的方法,也可以创建对象,调用对象的方法。如果有需要,还可以直接注册Java Method到函数管理器中。

11: 大数值计算(始于0.9版本)

Fel发布后,有些网友希望提供大数值计算功能,于是,大数值计算功能就有了。例子如下:

FelEngine fel = FelBuilder.bigNumberEngine();
String input = "111111111111111111111111111111+22222222222222222222222222222222";
Object value = fel.eval(input);
Object compileValue = fel.compile(input, fel.getContext()).eval(fel.getContext()); System.out.println("大数值计算(解释执行):" + value);
System.out.println("大数值计算(编译执行):" + compileValue);

由上例子可以看出,大数值计算引擎和常规计算引擎在使用方法是相同的。如果表达式数值比较大,要求精度高,可使用大数值计算引擎。不足之处是效率没有常规计算引擎高。

12: 安全(始于0.8版本)

为了防止出现“${'System'}.exit(1)”这样的表达式导致系统崩溃。Fel加入了安全管理器,主要是对方法访问进行控制。安全管理器中通过允许访问的方法列表(白名单)和禁止访问的方法列表(黑名单)来控制方法访问。将"java.lang.System. *"加入到黑名单,表示System类的所有方法都不能访问。将"java.lang.Math. *"加入白名单,表示只能访问Math类中的方法。如果你不喜欢这个安全管理器,可以自己开发一个,非常简单,只需要实现一个方法就可以了。

样例:

import com.greenpineyu.fel.FelEngine;
import com.greenpineyu.fel.common.FelBuilder;
import com.greenpineyu.fel.context.FelContext;
import com.greenpineyu.fel.function.CommonFunction;
import com.greenpineyu.fel.parser.FelNode; import java.math.BigDecimal; public class SumFunction extends CommonFunction { private FelContext felContext; @Override
public String getName() {
return "SUM";
} @Override
public Object call(FelNode felNode, FelContext felContext) {
this.felContext = felContext;
return super.call(felNode, felContext);
} @Override
public Object call(Object[] objects) {
if (objects == null || objects.length != 3) {
throw new IllegalArgumentException("SUM Function Argument Illegal");
}
Object express = objects[0];
Object beginIndex = objects[1];
Object endIndex = objects[2];
System.out.println("\nStart SumFunction: origin express=" + express + "; beginIndex=" + beginIndex + "; endIndex=" + endIndex);
int beginIndexInteger = Integer.valueOf(objects[1].toString());
int endIndexInteger = Integer.valueOf(objects[2].toString());
String expressString = express.toString(); BigDecimal sum = BigDecimal.ZERO; FelEngine felEngine = FelBuilder.bigNumberEngine();
felEngine.setContext(felContext);
for (int i = beginIndexInteger; i <= endIndexInteger; i++) {
String expressReal = expressString.replaceAll("_", "_" + i);
Object eval = felEngine.eval(expressReal);
System.out.println("expressReal=" + expressReal + "; index=" + i + "; eval=" + eval);
sum = sum.add(new BigDecimal(eval.toString()));
System.out.println("SumIterator=" + sum);
} double sumFunctionResult = sum.doubleValue();
System.out.println("End SumFunction: result=" + sumFunctionResult + "\n");
return sumFunctionResult;
} }

import com.greenpineyu.fel.FelEngine;
import com.greenpineyu.fel.FelEngineImpl;
import com.greenpineyu.fel.common.FelBuilder;
import com.greenpineyu.fel.context.FelContext;
import org.junit.Test; public class SumFunctionTest { @Test
public void test2() { FelEngine felEngine = new FelEngineImpl();
FelContext context = felEngine.getContext();
context.set("v", "aaa"); // Object evalRt = felEngine.eval("'aa'.equals(v)?'找到a':'没找到a'");
Object evalRt = felEngine.eval("v!=null && 'aa'.equals(v)?'找到a':'没找到a'");
Object evalR2t = felEngine.eval("v!=null && 'aa'.equals(v)?'找到a':999");
System.out.println( evalRt );
System.out.println( evalR2t ); }
public void test1() {
FelEngine felEngine = FelBuilder.bigNumberEngine();
felEngine.addFun(new SumFunction()); FelContext context = felEngine.getContext();
for (int i = 1; i <= 5; i++) {
context.set("x_" + i, i);
} String exp = "1+2*(SUM('x_*3+1','1','5')+1)";
Object eval = felEngine.eval(exp, context);
System.out.println("TEST SUM= " + eval);
} } import com.greenpineyu.fel.FelEngine;
import com.greenpineyu.fel.FelEngineImpl;
import com.greenpineyu.fel.common.FelBuilder;
import com.greenpineyu.fel.context.FelContext;
import org.junit.Test; public class SumFunctionTest { @Test
public void test2() { FelEngine felEngine = new FelEngineImpl();
FelContext context = felEngine.getContext();
context.set("v", "aaa"); // Object evalRt = felEngine.eval("'aa'.equals(v)?'找到a':'没找到a'");
Object evalRt = felEngine.eval("v!=null && 'aa'.equals(v)?'找到a':'没找到a'");
Object evalR2t = felEngine.eval("v!=null && 'aa'.equals(v)?'找到a':999");
System.out.println( evalRt );
System.out.println( evalR2t ); }
public void test1() {
FelEngine felEngine = FelBuilder.bigNumberEngine();
felEngine.addFun(new SumFunction()); FelContext context = felEngine.getContext();
for (int i = 1; i <= 5; i++) {
context.set("x_" + i, i);
} String exp = "1+2*(SUM('x_*3+1','1','5')+1)";
Object eval = felEngine.eval(exp, context);
System.out.println("TEST SUM= " + eval);
} }

FEL - Fast Expression Language的更多相关文章

  1. EL表达式Expression Language

    表达式语言Expression Language目的:简化jsp代码 EL内置对象 1.pageContext2.pageScope3.requestScope4.sessionScope5.appl ...

  2. SPEL语言-Spring Expression Language

    Spring表达式语言全称为"Spring Expression Language",缩写为"SpEL",类似于Struts 2x中使用的OGNL表达式语言,能 ...

  3. SPRING IN ACTION 第4版笔记-第四章ASPECT-ORIENTED SPRING-004-使用AspectJ’s pointcut expression language定义Pointcut

    一. 1.在Spring中,pointcut是通过AspectJ’s pointcut expression language来定义的,但spring只支持它的一部分,如果超出范围就会报Illegal ...

  4. Java EE Expression Language

    什么是EL? 形如这样(立即执行的): ${sessionScope.cart.total} 或者这样(延迟执行的): #{customer.name} 的表达式语言(Expression Langu ...

  5. EL(Expression Language)表达式语言

    EL(Expression Language)表达式语言 EL的基本语法是以${开始,以}结束 为了能够方便地输出数据,EL提供了11个内置对象,其中 2个内置对象为了方便输出请求参数 param用来 ...

  6. Spring生态研习【二】:SpEL(Spring Expression Language)

    1. SpEL功能简介 它是spring生态里面的一个功能强大的描述语言,支在在运行期间对象图里面的数据查询和数据操作.语法和标准的EL一样,但是支持一些额外的功能特性,最显著的就是方法调用以及基本字 ...

  7. EL(Expression Language)和JSTL标签(JSP Standard Tag Library)

    一.EL表达式: Expression Language提供了在 JSP 脚本编制元素范围外(例如:脚本标签)使用运行时表达式的功能.脚本编制元素是指页面中能够用于在JSP 文件中嵌入 Java代码的 ...

  8. SQL Expression Language Tutorial 学习笔记二

    11. Using Textual SQL 直接使用 SQL 如果实在玩不转, 还是可以通过 test() 直接写 SQL. In [51]: s = text( ...: "SELECT ...

  9. SQL Expression Language Tutorial 学习笔记一

    http://docs.sqlalchemy.org/en/latest/core/tutorial.html Google 翻译了一下 SQLAlchemy Expression Language, ...

  10. 应用于Java中的一个开源的表达式语言(Expression Language)

    OGNL(英文全称:Object Graph Navigation Language,中文名:对象导航图语言)是应用于Java中的一个开源的表达式语言(Expression Language),它被集 ...

随机推荐

  1. 一步步教你在 Windows 上构建 dotnet 系应用的 UOS 软件安装包

    本文将详细指导大家如何逐步为 dotnet 系列应用创建满足 UOS 统信系统软件安装包的要求.在这里,我们所说的 dotnet 系列应用是指那些能够在 Linux 平台上构建 UI 框架的应用,包括 ...

  2. ChatGPT,我的 .NET 应用该选择哪种日志框架?

    When using .NET 6, compare different main stream third-party logging packages, and give suggestions ...

  3. 微软开源 MS-DOS「GitHub 热点速览」

    上周又是被「大模型」霸榜的一周,各种 AI.LLM.ChatGPT.Sora.RAG 的开源项目在 GitHub 上"争相斗艳".这不 Meta 刚开源 Llama 3 没几天,苹 ...

  4. 视频讲解如何构建surging微服务调用

    surging 是一款优秀的微服务引擎,包括了社区版,标准版,异构版,平台版本来解决公司的业务场景需求,如果你是初学者,或者是技术狂热者,社区版完全可以符合你们的要求来学习或者构建起微服务体系的引擎框 ...

  5. scp本地服务器和远程服务器拷贝文件

    上传本地文件到服务器 scp 本地路径 用户名@远程服务器ip:远程路径 下载文件 scp 用户名@远程服务器ip:远程路径 本地路径 -r 是上传下载本地目录到远程 远程文件

  6. Jetbrains系列产品最新激活方法[持续更新]

    Jetbrains系列产品最新激活方法[持续更新] 2021.3.4系列激活 方法一: 2021.3.4参考文章: https://www.exception.site/essay/how-to-fr ...

  7. GCC编译选项含义解析

    GCC 编译 GCC的编译流程分为四个步骤,分别为: 预处理(Pre-Processing) 可以通过gcc -E hello.cc -o hello.i查看中间结果 编译(Compiling) 汇编 ...

  8. WEB服务与NGINX(23)- nginx的四层负载均衡功能

    目录 1. nginx实现四层的负载均衡 1.1 nginx四层负载配置参数 1.2 负载均衡示例-mariadb 1. nginx实现四层的负载均衡 nginx从1.9.0版本开始支持TCP模式的负 ...

  9. FE知识点(硕哥)

    目录 前传: 1.typeof和类型转换 正文: 1.作用域.作用域链([[scope]]) 2.立即执行函数 3.闭包 4.对象.包装类 5.原型原型链 6.call.apply 7.继承模式.命名 ...

  10. Vue3 项目

    创建 Vue3 项目的步骤如下: 安装 Node.js Vue3 需要依赖 Node.js 环境,因此需要先安装 Node.js.可以从官网下载 Node.js 的安装包并安装,也可以使用包管理器安装 ...