锁定老帖子 主题:Java脚本技术应用实例
精华帖 (0) :: 良好帖 (0) :: 新手帖 (11) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2011-01-22
最后修改:2011-01-25
前言一直以来都很喜欢可以自由扩展的软件,这一点应该已经在很多文章里提到,也重复过很多次了。但是,可扩展性,灵活性是开发人员最喜欢的东西了,本性难改。平时使用的开发环境如vim/emacs, IDE中的Eclipse/Netbeans, 浏览器FF/Chrome都具有强大而灵活的可扩展支持。而关于Java的脚本支持,我已经在数篇文章中提及,大多是关于JavaScript引擎rhino和宿主Java之间的合成,但是Java的脚步支持远不止这些,这篇文章尝试讨论一下,Java对其他语言的支持。
文中实现一个简单的工资计算器,本来是在来到新公司不久,用以和同事们交流脚本技术的应用时做的,后来又进行了一些改动,由于只是一个示例,界面很简单:
工资计算器这个计算器很简单,从脚本中获取转换表(convertTable)及基数(base),即:当工资低于base时,直接返回工资数目,如果高于base,则根据转换表来查找税率,然后扣除税款,得到实际工资。由于在实际生活中,税率会不断的调整,这部分内容就应该放入脚本:
//base salary var base = 2000; /** * range and tax-rage convert table */ var convertTable = { "0~1000" : 0.1, "1000~2000" : 0.15, "2000~3000" : 0.2, "3000~5000" : 0.25, "5000~8000" : 0.3, "8000~-1" : 0.4 }
这里用-1表示不限。
在本文的示例中,每一个脚本会被作为一个"插件",插件可以被创建,激活,安装到应用程序中,安装之后的插件,存在于应用程序的运行时环境(RuntimeEnv),并可以在需要的时候被调用执行。比如在本文中,初始化应用程序的时候,init方法将被调用:
public void init(){ Plugin system = new SimplePlugin("scripts/calc.js"); system.activate(); SimplePluginManager.getInstance().install(system); }
然后,当按钮[计算实际工资 ]被点击的时候,会调用:
btnCalc.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent e) { SimplePluginManager.getInstance().getPlugin("calc").activate(); String salary = textSalary.getText(); Double d = Double.parseDouble(salary); Double r = (Double)RuntimeEnv.getInstance().invokeFunction("calc", d); textReal.setText(String.valueOf(r)); } });
仔细观察会发现,在actionPerformed方法中,首先会将get出来的插件做激活(activate)动作,这是因为,如果应用程序在运行期间,脚本做过修改,则可以事实的反映在结果上。这里去掉了一些验证,比如根据脚本文件的lastModified 来判断是否需要激活等。
我将script做了一个简单的包装,成为插件,这个示例中的插件结构如上图所示。RuntimeEnv 为一个单例的实例,在应用中是始终有一个,每个组件都可以向这个实例请求执行脚本中的函数,至于函数的参数传递,类型转换等工作,由底层的脚本引擎来负责执行。
为了脚本可以被重复使用,可以将脚本先编译为“已编译脚本”对象:
/** * compile the script-file into an <code>CompiledScript</code> object * @return */ public CompiledScript compile(File file){ Date scriptDate = new Date(file.lastModified()); if(lastModified == null || scriptDate.after(lastModified)){ Reader reader = null; try { reader = new FileReader(file); compiledScript = RuntimeEnv.getInstance().getCompilableEngine().compile(reader); lastModified = scriptDate; } catch (FileNotFoundException e) { e.printStackTrace(); } catch (ScriptException e) { e.printStackTrace(); }finally{ if(reader != null){ try { reader.close(); } catch (IOException e) {} } } } this.status = Plugin.STATUS_LOADED; return compiledScript; }
JavaScript版本的实现下面为JavaScript版本的脚本,包含完整的计算逻辑,如果税率有新的调整,则仅仅需要修改脚本文件,甚至是在应用程序已处于运行状态。
//base salary var base = 2000; /** * range and tax-rage convert table */ var convertTable = { "0~1000" : 0.1, "1000~2000" : 0.15, "2000~3000" : 0.2, "3000~5000" : 0.25, "5000~8000" : 0.3, "8000~-1" : 0.4 } /** * if the value is in the range? */ function inRange(value, range){ var vs = range.split("~"); var low, high; low = parseFloat(vs[0]); high = parseFloat(vs[1]); if(high == -1){//-1 means infinity high = Number.MAX_VALUE; } return (value >= low && value <= high); } /** * This is the function will be invoked by java program * @param salary * @return */ function calc(salary){ var value; // less that or equals to base if(salary <= base){ value = salary; }else{ var f = salary - base; for(var item in convertTable){ if(inRange(f, item)){ value = salary - salary * convertTable[item]; break; } } } return value; }
Python版本的实现python版本仅仅作为JavaScript版本的翻译,但是在应用程序的角度来看,是没有任何差别的(可能在有错误的时候,会产生不同的异常)。
# # author : juntao.qiu@gmail.com # # salary base base = 2000 # convert table of range and rate convertTable = { "0~1000" : 0.1, "1000~2000" : 0.15, "2000~3000" : 0.2, "3000~5000" : 0.25, "5000~8000" : 0.3, "8000~-1" : 0.4 } # test value in range or not def inRange(value, range): vs = range.split("~") low = float(vs[0]) high = float(vs[1]) if(high == -1): high = "inf" return (value >= low and value <= high) # calculate salary without tax, invoked by java code def calc(salary): value = None if(salary <= base): return salary else: f = salary - base for item in convertTable: if(inRange(f, item)): value = salary - salary * convertTable[item] break; return value
应该注意的是,jython的版本应该等于或者大于2.5.1,在2.5.1中,jython才实现了java的脚本扩展接口,我在测试的时候,jython的最新版本为2.5.1,不知道现在是否已经更新。
========================================================= 更新:
声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2011-01-22
这位兄弟,所得税不是这么计算的啦...
不管你代码写的多优雅,注释写的多好,设计用了多少UML图,设计得多灵活,你的业务错掉了,对客户来说你的软件就是一分不值,哈哈。 |
|
返回顶楼 | |
发表时间:2011-01-22
lugionline 写道 这位兄弟,所得税不是这么计算的啦...
不管你代码写的多优雅,注释写的多好,设计用了多少UML图,设计得多灵活,你的业务错掉了,对客户来说你的软件就是一分不值,哈哈。 呵呵,这个只是凭借我自己的想法做的,我个人根本不了解所得税是怎么算的,只是为了演示Java脚本的使用(JavaScript,Python等脚本),见笑,见笑。 |
|
返回顶楼 | |
发表时间:2011-01-23
楼主的这个想法很好。
将插件机制和动态语言结合起来到时候个很好的做法 不知有代码share否? |
|
返回顶楼 | |
发表时间:2011-01-23
kaneg 写道 楼主的这个想法很好。
将插件机制和动态语言结合起来到时候个很好的做法 不知有代码share否? 不好意思,之前忘记附代码了,更新后已经加到了文章的后面,如有需要请自行下载。 |
|
返回顶楼 | |
发表时间:2011-01-23
复杂脚本能支持吗?
|
|
返回顶楼 | |
发表时间:2011-01-23
glovebx 写道 复杂脚本能支持吗?
你是说例子中的计算器是否支持复杂脚本吗?由于内部使用的rhino引擎,并没有进行任何方面的限制,因此理论上,可以支持任意复杂的脚本。比如自己用JavaScript来实现OO语言系统,利用宿主语言Java的多线程,UI等便利来改善JS应用的性能和外观等。 |
|
返回顶楼 | |
发表时间:2011-01-23
动态脚本执行的效率较低,尤其是js在java高并发的时候
|
|
返回顶楼 | |
发表时间:2011-01-23
楼主现在是2011年了,呵呵~~
|
|
返回顶楼 | |
发表时间:2011-01-24
楼主这个思路不错
好好研究一下 |
|
返回顶楼 | |