本文共 18516 字,大约阅读时间需要 61 分钟。
Fel是轻量级的高效的表达式计算引擎
更多干货
Fel在源自于企业项目,设计目标是为了满足不断变化的功能需求和性能需求。
Fel是开放的,引擎执行中的多个模块都可以扩展或替换。Fel的执行主要是通过函数实现,运算符(+、-等都是Fel函数),所有这些函数都是可以替换的,扩展函数也非常简单。
Fel有双引擎,同时支持解释执行和编译执行。可以根据性能要求选择执行方式。编译执行就是将表达式编译成字节码(生成java代码和编译模块都是可以扩展和替换的)
Fel有多快?
通常情况下,Fel-0.7每秒可以执行千万次表达式(不包含编译时间)。速度是Jexl-2.0的20倍以上。
目前还没有发现开源的表达式引擎比Fel快。
具体的数据请参见。
为何要使用Fel?
Fel语法和API非常简单,语法与基本相同,几乎没有学习成本。
Fel非常快,上面已经做了简单说明。
Fel整个包只有200多KB。
Fel可以非常方便的访问数组、集合、Map的元素和对象的属性。
Fel可以非常方便的调用对象的方法和类方法(如果这些还不够,可以添加自定义函数)。
Fel支持大数值高精度计算
Fel有良好的安全管理功能
如果Fel不能满足你的要求,扩展和修改Fel很简单。
Fel不能做什么?
Fel只支持表达式,不支持脚本。
Fel适用场景:
Fel适合处理海量数据,Fel良好的扩展性可以更好的帮助用户处理数据。
Fel同样适用于其他需要使用表达式引擎的地方(如果工作流、公式计算、数据有效性校验等等)
安装
1:获取Fel
项目主页: 下载地址:
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<String,String> m = new HashMap<String,String>();
- m.put("ElName", "fel");
- ctx.set("m",m);
-
-
- Object result = fel.eval("foo.size");
-
-
- result = fel.eval("foo.sample");
-
-
-
- result = fel.eval("foo.name");
-
-
- result = fel.eval("m.ElName");
4:访问数组、集合、Map
- FelEngine fel = new FelEngineImpl();
- FelContext ctx = fel.getContext();
-
-
- int[] intArray = { 1,2,3};
- ctx.set("intArray",intArray);
-
- String exp = "intArray[0]";
- System.out.println(exp+"->"+fel.eval(exp));
-
-
- List<Integer> list = Arrays.asList(1,2,3);
- ctx.set("list",list);
-
- exp = "list[0]";
- System.out.println(exp+"->"+fel.eval(exp));
-
-
- Collection<String> coll = Arrays.asList("a","b","c");
- ctx.set("coll",coll);
-
- exp = "coll[0]";
- System.out.println(exp+"->"+fel.eval(exp));
-
-
- Iterator<String> iterator = coll.iterator();
- ctx.set("iterator", iterator);
-
- exp = "iterator[0]";
- System.out.println(exp+"->"+fel.eval(exp));
-
-
- Map<String,String> m = new HashMap<String, String>();
- 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<int[]> listArray = new ArrayList<int[]>();
- 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:自定义函数
-
- Function fun = new CommonFunction() {
-
- public String getName() {
- return "hello";
- }
-
-
-
-
- @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提供的$函数可以方便的调用工具类的方法 熟悉的朋友肯定知道"$"函数的威力。Fel东施效颦,也实现了一个"$"函数,其作用是获取class和创建对象。结合点操作符,可以轻易的调用工具类或对象的方法。
-
- FelEngine.instance.eval("$('Math').min(1,2)");
-
- FelEngine.instance.eval("$('com.greenpineyu.test.Foo.new').toString());
通过"$('class').method"形式的语法,就可以调用任何等三方类包(commons lang等)及自定义工具类的方法,也可以创建对象,调用对象的方法。如果有需要,还可以直接注册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);
由上例子可以看出,大数值计算引擎和常规计算引擎在使用方法是相同的。如果表达式数值比较大,要求精度高,可使用大数值计算引擎。不足之处是效率没有常规计算引擎高。
安全(始于0.8版本)
为了防止出现“${'System'}.exit(1)”这样的表达式导致系统崩溃。Fel加入了安全管理器,主要是对方法访问进行控制。安全管理器中通过允许访问的方法列表(白名单)和禁止访问的方法列表(黑名单)来控制方法访问。将"java.lang.System. * "加入到黑名单,表示System类的所有方法都不能访问。将"java.lang.Math. * "加入白名单,表示只能访问Math类中的方法。如果你不喜欢这个安全管理器,可以自己开发一个,非常简单,只需要实现一个方法就可以了。
附基本Java工程源代码:
Example类:
- public class Example {
-
- public static void main(String[] args) {
-
- System.out.println("-----------1.入门---------");
- helloworld();
-
- System.out.println("-----------2.使用变量---------");
- useVariable();
-
- System.out.println("-----------3.获取对象属性---------");
- getAttr();
-
- System.out.println("---------4.调用对象的方法-----------");
- callMethod();
-
- System.out.println("--------5.访问数组、集合------------");
- visitColl();
-
- System.out.println("--------6.自定义上下文环境------------");
- context();
-
- System.out.println("--------7.多层次上下文环境(变量命名空间)------------");
- contexts();
-
- System.out.println("---------8.大数值计算-----------");
- testBigNumber();
-
- System.out.println("----------9.函数----------");
- userFunction();
-
- System.out.println("---------10.自定义 解释器-----------");
- userInterpreter();
-
- System.out.println("----------11.操作符重载----------");
- operatorOverload();
-
- System.out.println("----------12.速度测试----------");
- testSpeed();
-
- System.out.println("----------13.静态方法----------");
- staticMethod();
- }
-
-
-
-
- public static void helloworld() {
-
- Object result = FelEngine.instance.eval("5000*12+7500");
- System.out.println(result);
- }
-
-
-
-
- public static void useVariable() {
- FelEngine fel = getEngine();
- FelContext ctx = fel.getContext();
- ctx.set("单价", 5000);
- ctx.set("数量", 12);
- ctx.set("运费", 7500);
- Object result = fel.eval("单价*数量+运费");
- System.out.println(result);
- }
-
-
-
-
- public static void getAttr() {
- FelEngine fel = getEngine();
- FelContext ctx = fel.getContext();
- Foo foo = new Foo();
- ctx.set("foo", foo);
- Map<String, String> m = new HashMap<String, String>();
- m.put("ElName", "fel");
- ctx.set("m", m);
-
-
- Object result = fel.eval("foo.size");
- System.out.println(result);
-
- result = fel.eval("foo.sample");
- System.out.println(result);
-
-
- result = fel.eval("foo.name");
- System.out.println(result);
-
- result = fel.eval("m.ElName");
- System.out.println(result);
- }
-
-
-
-
- public static void callMethod() {
- FelEngine fel = getEngine();
- FelContext ctx = fel.getContext();
- ctx.set("out", System.out);
- fel.eval("out.println('Hello Everybody'.substring(6))");
- }
-
-
-
-
- public static void visitColl() {
- FelEngine fel = getEngine();
- FelContext ctx = fel.getContext();
-
-
- int[] intArray = { 1, 2, 3 };
- ctx.set("intArray", intArray);
-
- String exp = "intArray[0]";
- System.out.println(exp + "->" + fel.eval(exp));
-
-
- List<Integer> list = Arrays.asList(1, 2, 3);
- ctx.set("list", list);
-
- exp = "list[0]";
- System.out.println(exp + "->" + fel.eval(exp));
-
-
- Collection<String> coll = Arrays.asList("a", "b", "c");
- ctx.set("coll", coll);
-
- exp = "coll[0]";
- System.out.println(exp + "->" + fel.eval(exp));
-
-
- Iterator<String> iterator = coll.iterator();
- ctx.set("iterator", iterator);
-
- exp = "iterator[0]";
- System.out.println(exp + "->" + fel.eval(exp));
-
-
- Map<String, String> m = new HashMap<String, String>();
- m.put("name", "Wangxiaoming");
- 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<int[]> listArray = new ArrayList<int[]>();
- 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));
- }
-
-
-
-
- public static void context() {
-
- FelContext ctx = new AbstractContext() {
- @Override
- public Object get(String name) {
- if ("天气".equals(name)) {
- return "晴";
- }
- if ("温度".equals(name)) {
- return 25;
- }
- return null;
- }
-
- };
- FelEngine fel = new FelEngineImpl(ctx);
- String exp = "'天气-----:'+天气+';温度------:'+温度";
- Object eval = fel.compile(exp, ctx).eval(ctx);
- System.out.println(eval);
- }
-
-
-
-
- public static void contexts() {
- FelEngine fel = getEngine();
- 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);
- }
-
-
-
-
- public static void testBigNumber() {
-
- 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);
- }
-
-
-
-
- public static void userFunction() {
-
- Function fun = new CommonFunction() {
-
- @Override
- public String getName() {
- return "hello";
- }
-
-
-
-
- @Override
- public Object call(Object[] arguments) {
- Object msg = null;
- if (arguments != null && arguments.length > 0) {
- msg = arguments[0];
- }
- return ObjectUtils.toString(msg);
- }
-
- };
- FelEngine e = getEngine();
-
- 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);
- }
-
-
-
-
- public static void testCompileX() {
- FelEngine fel = getEngine();
- String exp = "单价*数量";
- final MutableInt index = new MutableInt(0);
-
-
- final int[] price = new int[] { 2, 3, 4 };
-
- final double[] number = new double[] { 10.99, 20.99, 9.9 };
- FelContext context = new AbstractContext() {
-
- @Override
- public Object get(String name) {
- if ("单价".equals(name)) {
- return price[index.intValue()];
- }
- if ("数量".equals(name)) {
- return number[index.intValue()];
- }
- return null;
- }
- };
- Expression compExp = fel.compile(exp, context);
- for (int i = 0; i < number.length; i++) {
- index.setValue(i);
- Object eval = compExp.eval(context);
- System.out.println("总价[" + price[i] + "*" + number[i] + "=" + eval
- + "]");
- }
- }
-
-
-
-
- public static void userInterpreter() {
- FelEngine fel = getEngine();
- String costStr = "成本";
- FelContext rootContext = fel.getContext();
- rootContext.set(costStr, "60000");
- FelNode node = fel.parse(costStr);
-
- node.setInterpreter(new ConstInterpreter(rootContext, node));
- System.out.println(node.eval(rootContext));
- }
-
-
-
-
- public static void operatorOverload() {
-
-
-
-
- FelEngine fel = getEngine();
-
- double[] price = new double[] { 2, 3, 4 };
-
- double[] cost = new double[] { 0.3, 0.3, 0.4 };
- FelContext ctx = fel.getContext();
- ctx.set("单价", price);
- ctx.set("费用", cost);
- String exp = "单价+费用";
- Interpreters interpreters = new Interpreters();
-
- interpreters.add("+", new Interpreter() {
- @Override
- public Object interpret(FelContext context, FelNode node) {
- List<FelNode> args = node.getChildren();
- double[] leftArg = (double[]) args.get(0).eval(context);
- double[] rightArg = (double[]) args.get(1).eval(context);
- return sum(leftArg) + sum(rightArg);
- }
-
-
- public double sum(double[] array) {
- double d = 0;
- for (int i = 0; i < array.length; i++) {
- d += array[i];
- }
- return d;
- }
- });
-
-
- Expression expObj = fel.compile(exp, null, interpreters);
- Object eval = expObj.eval(ctx);
- System.out.println("数组相加:" + eval);
- }
-
-
-
-
- public static void testSpeed() {
- FelEngine fel = getEngine();
- String exp = "40.52334+60*(21.8144+17*32.663)";
- FelNode node = fel.parse(exp);
- int times = 1000;
- long s1 = System.currentTimeMillis();
- for (int i = 0; i < times; i++) {
-
- node.eval(null);
- }
- long s2 = System.currentTimeMillis();
- System.out.println("花费的时间:" + (s2 - s1));
- }
-
-
-
-
- public static void massData() {
- FelEngine fel = getEngine();
- final Interpreters opti = new Interpreters();
- final MutableInt index = new MutableInt(0);
- int count = 10 * 1000 * 1000;
- final double[] counts = new double[count];
- final double[] prices = new double[count];
- Arrays.fill(counts, 10d);
- Arrays.fill(prices, 2.5d);
- opti.add("单价", new Interpreter() {
- @Override
- public Object interpret(FelContext context, FelNode node) {
- return prices[index.intValue()];
- }
- });
- opti.add("数量", new Interpreter() {
- @Override
- public Object interpret(FelContext context, FelNode node) {
- return counts[index.intValue()];
- }
- });
- Expression expObj = fel.compile("单价*数量", null, opti);
- long start = System.currentTimeMillis();
- Object result = null;
- for (int i = 0; i < count; i++) {
- result = expObj.eval(null);
- index.increment();
- }
- long end = System.currentTimeMillis();
-
- System.out.println("大数据量计算:" + result + ";耗时:" + (end - start));
- }
-
-
-
-
-
-
-
-
-
-
- public static void staticMethod() {
-
- System.out.println(FelEngine.instance.eval("$('Math').max(1,3)"));
-
- System.out.println(FelEngine.instance
- .eval("$('com.ebiz.fel.Foo.new').toString()"));
- }
-
- private static FelEngine getEngine() {
- return FelBuilder.engine();
- }
-
- }
-
- class ColumnInterpreter implements Interpreter {
- MutableInt index;
-
- double[] records;
-
- ColumnInterpreter(MutableInt index, double[] records) {
- this.index = index;
- this.records = records;
- }
-
- @Override
- public Object interpret(FelContext context, FelNode node) {
- return records[index.intValue()];
- }
- }
-
- class MutableInt {
- private int value;
-
- public MutableInt(int i) {
- this.value = i;
- }
-
- public int intValue() {
- return value;
- }
-
- public void setValue(int i) {
- this.value = i;
- }
-
- public void increment() {
- value++;
- }
- }
Foo类:
- import java.util.ArrayList;
- import java.util.Iterator;
- import java.util.List;
-
- public class Foo {
-
- private final String name;
-
- private Foo foo = null;
-
- static private Foo[] f = new Foo[] { new Foo("array0"), new Foo("array1") };
-
- static private Foo[] fooes = f;
-
- public Foo[] getFooes() {
- return fooes;
- }
-
- public void setFooes(Foo[] fooes) {
- this.fooes = fooes;
- }
-
- private boolean beenModified = false;
- private String property1 = "some value";
-
- public Foo(String name) {
- this.name = name;
- }
-
- public Foo() {
- this("anonymity");
- }
-
- public static String sayHello(String str) {
- return "hello" + str;
- }
-
- public class Cheezy {
- public Iterator<String> iterator() {
- return getCheeseList().iterator();
- }
- }
-
- public String get(String arg) {
- if ("name".equals(arg)) {
- return name;
- }
- return "can't find " + arg;
- }
-
- public String convertBoolean(boolean b) {
- return "Boolean : " + b;
- }
-
- public int getCount() {
- return 5;
- }
-
- public String contact(String a, String b, String c, String d) {
- return a + b + c + c;
- }
-
- public List<String> getCheeseList() {
- ArrayList<String> answer = new ArrayList<String>();
- answer.add("cheddar");
- answer.add("edam");
- answer.add("brie");
- return answer;
- }
-
- public Cheezy getCheezy() {
- return new Cheezy();
- }
-
- public boolean isSimple() {
- return true;
- }
-
- public int square(int value) {
- return value * value;
- }
-
- public boolean getTrueAndModify() {
- beenModified = true;
- return true;
- }
-
- public boolean getModified() {
- return beenModified;
- }
-
- public int getSize() {
- return 22;
- }
-
- public String getProperty1() {
- return property1;
- }
-
- public void setProperty1(String newValue) {
- property1 = newValue;
- }
-
- public Foo getFoo() {
- return this.foo;
- }
-
- public void setFoo(Foo foo) {
- this.foo = foo;
- }
-
- @Override
- public String toString() {
- return this.name;
- }
- }
运行结果:
- -----------1.入门---------
- 67500
- -----------2.使用变量---------
- 67500
- -----------3.获取对象属性---------
- 22
- can't find sample
- anonymity
- fel
- ---------4.调用对象的方法-----------
- Everybody
- --------5.访问数组、集合------------
- intArray[0]->1
- list[0]->1
- coll[0]->a
- iterator[0]->a
- map.name->Wangxiaoming
- intArrays[0][0]->11
- listArray[0][0]->1
- --------6.自定义上下文环境------------
- 天气-----:晴;温度------:25
- --------7.多层次上下文环境(变量命名空间)------------
- 期望利润:50
- 实际利润:30
- ---------8.大数值计算-----------
- 大数值计算(解释执行):22333333333333333333333333333333
- 大数值计算(编译执行):22333333333333333333333333333333
- ----------9.函数----------
- hello fel
- hello fel
- ---------10.自定义 解释器-----------
- 60000
- ----------11.操作符重载----------
- 数组相加:10.0
- ----------12.速度测试----------
- 花费的时间:91
- ----------13.静态方法----------
- 3
- anonymity
转载地址:http://mdlnn.baihongyu.com/