【Java】Fel计算引擎学习
⼀、前⾔
最近的项⽬中,有⼀个这种需求,系统中配置很多的公式,每个公式中都会有因⼦。⽐如:本⾦*利息,计算的时候,把这两个因⼦替换掉,如,100*0.01,然后通过java来计算出结果。
⼆、实现的⽅式
为了解决java直接读取字符串,然后进⾏计算出结果。我总结了以下⼏种⽅法:
ScriptEngineManager实现字符串公式灵活计算
Spring 表达式语⾔之 SpEL 语法
Fel是轻量级的⾼效的表达式计算引擎
……
三、介绍⼀下Fel的使⽤
Fel是轻量级的⾼效的表达式计算引擎
Fel在源⾃于企业项⽬,设计⽬标是为了满⾜不断变化的功能需求和性能需求。
Fel是开放的,引擎执⾏中的多个模块都可以扩展或替换。Fel的执⾏主要是通过函数实现,运算符(+、-等都是Fel函数),所有这些函数都是可以替换的,扩展函数也⾮常简单。
Fel有双引擎,同时⽀持解释执⾏和编译执⾏。可以根据性能要求选择执⾏⽅式。编译执⾏就是将表达式编译成字节码(⽣成java代码和编译模块都是可以扩展和替换的)
Fel有多快?
通常情况下,Fel-0.7每秒可以执⾏千万次表达式(不包含编译时间)。速度是Jexl-2.0的20倍以上。
⽬前还没有发现开源的表达式引擎⽐Fel快。
为何要使⽤Fel?
Fel语法和API⾮常简单,语法与Java基本相同,⼏乎没有学习成本。
Fel⾮常快,上⾯已经做了简单说明。
Fel整个包只有200多KB。
Fel可以⾮常⽅便的访问数组、集合、Map的元素和对象的属性。
Fel可以⾮常⽅便的调⽤对象的⽅法和类⽅法(如果这些还不够,可以添加⾃定义函数)。
Fel⽀持⼤数值⾼精度计算
Fel有良好的安全管理功能
如果Fel不能满⾜你的要求,扩展和修改Fel很简单。
Fel不能做什么?
Fel只⽀持表达式,不⽀持脚本。
Fel适⽤场景:
Fel适合处理海量数据,Fel良好的扩展性可以更好的帮助⽤户处理数据。
Fel同样适⽤于其他需要使⽤表达式引擎的地⽅(如果⼯作流、公式计算、数据有效性校验等等)安装
1:获取Fel
如果是maven项⽬,可以直接引⼊依赖:
<!-- mvnrepository/artifact/org.eweb4j/fel -->
<dependency>
<groupId>org.eweb4j</groupId>
<artifactId>fel</artifactId>
<version>0.8</version>
</dependency>
Fel使⽤例⼦:
1:算术表达式:
FelEngine fel = new FelEngineImpl();
Object result = fel.eval("5000*12+7500");
System.out.println(result);
输出结果:67500
2:变量
使⽤变量,其代码如下所⽰:
FelContext ctx = Context();
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 = Context();
Foo foo = new Foo();
ctx.set("foo", foo);
Map<String,String> m = new HashMap<String,String>();
m.put("ElName", "fel");
ctx.set("m",m);
//调⽤Size()⽅法。
Object result = fel.eval("foo.size");
/
/调⽤foo.isSample()⽅法。
result = fel.eval("foo.sample");
//foo没有name、getName、isName⽅法
//foo.name会调⽤("name")⽅法。
result = fel.eval("foo.name");
//m.ElName会调⽤m.get("ElName");
result = fel.eval("m.ElName");
4:访问数组、集合、Map
FelEngine fel = new FelEngineImpl();
FelContext ctx = Context();
//数组
int[] intArray = {1,2,3};
ctx.set("intArray",intArray);
//获取intArray[0]
String exp = "intArray[0]";
System.out.println(exp+"->"+fel.eval(exp));
//List
List<Integer> list = Arrays.asList(1,2,3);
ctx.set("list",list);
//获取(0)
exp = "list[0]";
System.out.println(exp+"->"+fel.eval(exp));
/
/集合
Collection<String> coll = Arrays.asList("a","b","c");
ctx.set("coll",coll);
//获取集合最前⾯的元素。执⾏结果为"a"
exp = "coll[0]";
System.out.println(exp+"->"+fel.eval(exp));
//迭代器
Iterator<String> iterator = coll.iterator();
ctx.set("iterator", iterator);
//获取迭代器最前⾯的元素。执⾏结果为"a"
exp = "iterator[0]";
System.out.println(exp+"->"+fel.eval(exp));
//Map
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 = Context();
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)){
return25;
}
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 = Context();
//⽗级上下⽂中设置成本和价格
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 = Context();
ctx.set("单价", 5000);
ctx.set("数量", 12);
ctx.set("运费", 7500);
Expression exp = felpile("单价*数量+运费",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")时执⾏的代码
*/学习java的学习方法
@Override
public Object call(Object[] arguments) {
Object msg = null;
if(arguments!= null && arguments.length>0){
msg = arguments[0];
}
String(msg);
}
};
FelEngine e = new FelEngineImpl();
//添加函数到引擎中。
e.addFun(fun);
String exp = "hello('fel')";
//解释执⾏
Object eval = e.eval(exp);
System.out.println("hello "+eval);
//编译执⾏
Expression compile = epile(exp, null);
eval = compile.eval(null);
System.out.println("hello "+eval);
执⾏结果:
hello fel hello fel
10:调⽤静态⽅法,⾃定义函数
如果你觉得上⾯的⾃定义函数也⿇烦,Fel提供的”函数的威⼒。Fel东施效颦,也实现了⼀个”$”函数,其作⽤是获取class和创建对象。结合点操作符,可以轻易的调⽤⼯具类或对象的⽅法。
//调⽤Math.min(1,2)
FelEngine.instance.eval("$('Math').min(1,2)");
//调⽤new Foo().toString();
FelEngine.instance.eval("$('w').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 = felpile(input, Context()).Context());
System.out.println("⼤数值计算(解释执⾏):" + value);
System.out.println("⼤数值计算(编译执⾏):" + compileValue);
由上例⼦可以看出,⼤数值计算引擎和常规计算引擎在使⽤⽅法是相同的。如果表达式数值⽐较⼤,要求精度⾼,可使⽤⼤数值计算引擎。不⾜之处是效率没有常规计算引擎⾼。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。