精华帖 (0) :: 良好帖 (6) :: 新手帖 (0) :: 隐藏帖 (0)
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
作者 | 正文 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
发表时间:2011-08-27
最后修改:2011-09-14
更新日志 2011-08-30:发布0.3.1版。解决在Tomcat下不能编译的问题 2011-08-30:发布0.3.2版。重构代码,加入编译优化功能。 2011-09-4:发布0.4版。全面支持函数、自定义生成源码、再次提升性能。 2011-09-12:发布0.4.1版。实现自适应编译器,在java1.5环境下,使用1.5的编译器(需要将tools.jar加入classpath)。在java1.6的环境下,使用1.6的编译器。 简介 Fel是简单的、高效的、易扩展的开源项目,使用java(jdk1.5)开发的开源项目。与java紧密结合,可以在Fel表达式中调用java方法(如:"abc".length(),"abc".substring(1)等)。 简单性:语法简单,与java表达式类似,支持中文变量和函数。其实现原理也非常简单,只要稍微有一些 java基础就可以看懂源码。 高效性:在使用编译模式的性能,基本上java执行表达式的性能相同。 易扩展:在执行表达式的主要环节(解析表达式、编译表达式、函数管理、变量管理)都有很高的可定制性。 Fel适合处理海量数据。它是功能强大的、实现简单、扩展性强的表达式语言。通过Fel表达式语言的实现原理创建一个新的表达式语言也不是难事。 快速入门 获取Fel 项目地址:http://code.google.com/p/fast-el/ 源代码:http://fast-el.googlecode.com/svn/trunk/ 安装部署 将fel-all-xx.jar加入classpath就可以使用。 如果需要使用编译功能,还需要加入tools.jar到classpath 功能 HelloWorld 执行四则表达式,代码如下所示: FelEngine fel = new FelEngineImpl(); Object result = fel.eval("5000*12+7500"); System.out.println(result); 输出结果:67500 变量 使用变量,其代码如下所示: FelContext ctx = fel.getContext(); ctx.set("单价", 5000); ctx.set("数量", 12); ctx.set("运费", 7500); Object result = fel.eval("单价*数量+运费"); System.out.println(result); 输出结果:67500 调用JAVA方法 FelEngine fel = new FelEngineImpl(); FelContext ctx = fel.getContext(); ctx.set("out", System.out); fel.eval("out.println('Hello Everybody'.substring(6))"); 输出结果:Everybody 自定义上下文环境 //负责提供气象服务的上下文环境 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 多层上下文环境(命名空间) 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 编译执行 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字节码执行速度一样快。 自定义函数 public static void userFunction(){ //定义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 操作符重载 /* * 扩展Fel的+运算符,使其支持数组+数组 */ FelEngine fel = new FelEngineImpl(); // 单价 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 = "单价+费用"; InteOpt interpreters = new InteOpt(); //定义"+"操作符的解释方法。 interpreters.add("+", new Interpreter() { 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) 执行结果:数组相加:10.0 附件fel最新版本和示例代码 未完待续 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||
返回顶楼 | |||||||||||||||||||||||||||||||||||||||||||||||||||||
发表时间:2011-08-27
最后修改:2011-09-05
性能测试
选用Jexl作为比较对象是对不住Jexl了。在Fel出现之前,使用的是Jexl,对它还是有感情的(Fel部分模块也借鉴了Jexl)。 Jexl毕竟没有将表达式编译成字节码,所以效率不是非常高,对比测试也有失公平。 当然,Fel在性能上并不是专找软柿子比较。 目前还没有发现开源的EL比Fel快。如果有,请告诉我,我也学习一下。 以下所有测试中的时间统计都没有包含编译时间 测试环境:
上下文环境 Map<String, Object> vars = new HashMap<String, Object>(); vars.put("i", 100); vars.put("pi", 3.14d); vars.put("d", -3.9); vars.put("b", (byte) 4); vars.put("bool", false); Map<String,Object> m = new HashMap<String, Object>(); m.put("d", 5); m.put(null,"this is null"); vars.put("m", m); vars.put("s","hello world"); 测试场景1:常量计算 表达式:1000+100.0*99-(600-3*15)%(((68-9)-3)*2-100)+10000%7*71 执行结果:11181.0
测试场景2:包含多个变量的布尔表达式 表达式:i * pi + (d * b - 199) / (1 - d * pi) - (2 + 100 - i / pi) % 99 ==i * pi + (d * b - 199) / (1 - d * pi) - (2 + 100 - i / pi) % 99 执行结果:true
测试场景3:包含多个变量的算术表达式 表达式:pi*d+b-(1000-d*b/pi)/(pi+99-i*d)-i*pi*d/b 执行结果:295.86196287059323
测试场景4:调用JAVA方法 表达式:s.substring(m.d) 执行结果: world
测试场景5:嵌套调用JAVA方法 表达式:s.substring(1).substring(2).indexOf('world') 执行结果:3
测试结论: 常量表达式:比Jexl快得太多了,主要是Fel对常量表达式作了优化,。 包含多个变量的表达式:Fel比Jexl快10倍甚至几十倍。Fel同样对变量访问作了优化。 字符串访问:Fel还是快20倍以上。 并发测试 测试环境概述:由于数据量比较大,加了内存参数 -Xmx1024M 测试线程数:50,100,200; 每线程执行表达式次数:100万 Jexl落败是意料之中的,不过,没有编译成字节码的Jexl执行速度有这么快,已经很不错了。 执行的表达式: "1000+100.0*99-(600-3*15)%(((68-9)-3)*2-100)+10000%7*71"; "i * pi + (d * b - 199) / (1 - d * pi) - (2 + 100 - i / pi) % 99 ==i * pi + (d * b - 199) / (1 - d * pi) - (2 + 100 - i / pi) % 99 "; "pi*d+b-(1000-d*b/pi)/(pi+99-i*d)-i*pi*d/b"; "pi*d+b-(1000-d*b/pi)/(pi+99-i*d)-i*pi*d/b"; "'hello world'.substring(1).substring(2).indexOf('world')"; 其中产生变量值的方式(使用自定义context) final FelContext ctx = new AbstractConetxt() { int index = 0; int size = 10; Object[] vars = new Object[size]; { for (int i = 0; i < vars.length; i++) { vars[i] = Math.random() * 100; } } //提供变量 public Object get(Object name) { return vars[index++ % size]; } };
并发测试总结:即使在并发的情况下,Fel执行表达式500万次,平均耗时也在1秒之内。 |
|||||||||||||||||||||||||||||||||||||||||||||||||||||
返回顶楼 | |||||||||||||||||||||||||||||||||||||||||||||||||||||
发表时间:2011-08-27
先睡了,静待意见建议。
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||
返回顶楼 | |||||||||||||||||||||||||||||||||||||||||||||||||||||
发表时间:2011-08-28
期待高并发下的测试,没有并发的测试意义不大
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||
返回顶楼 | |||||||||||||||||||||||||||||||||||||||||||||||||||||
发表时间:2011-08-28
最后修改:2011-08-28
另外jexl2是支持jsr223的,这个现在是什么情况
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||
返回顶楼 | |||||||||||||||||||||||||||||||||||||||||||||||||||||
发表时间:2011-08-28
另外,期待支持脚本的版本
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||
返回顶楼 | |||||||||||||||||||||||||||||||||||||||||||||||||||||
发表时间:2011-08-28
freish 写道 期待高并发下的测试,没有并发的测试意义不大
说得不错,请说明一下具体的测试场景,我再测一下。 freish 写道 另外jexl2是支持jsr223的,这个现在是什么情况
没有详细研究JSR223,有空再看一下。目前正在处理扩展性和性能等问题 freish 写道 另外,期待支持脚本的版本
其实也考虑过做支持脚本的版本,由于时间有限,还是先把表达式的版本做好做精吧。 目标是让FEL成可易扩展和维护的效率极高的表达式语言。 最后,谢谢你的回复。 |
|||||||||||||||||||||||||||||||||||||||||||||||||||||
返回顶楼 | |||||||||||||||||||||||||||||||||||||||||||||||||||||
发表时间:2011-08-28
担心性能的问题。
看起来挺好用 顶楼主 |
|||||||||||||||||||||||||||||||||||||||||||||||||||||
返回顶楼 | |||||||||||||||||||||||||||||||||||||||||||||||||||||
发表时间:2011-08-28
呵呵,也用了antlr ,有兴趣可以加入
61765717 群,专门讨论antlr 的地方 http://beetl.sourceforge.net/ 我写的模板语言,也用了antlr |
|||||||||||||||||||||||||||||||||||||||||||||||||||||
返回顶楼 | |||||||||||||||||||||||||||||||||||||||||||||||||||||
发表时间:2011-08-28
应用场景是什么呢?
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||
返回顶楼 | |||||||||||||||||||||||||||||||||||||||||||||||||||||