选择合适的Java脚本语言
--假如你正考虑在java应用中集成脚本解释器,最难得是决定使用那种
摘要:脚本语言已经向java开发者证实了它的价值。它让客户实现应用功能的扩展和界面的个性化,从而程序的价值得以提升。另外,它们可以显著的简化程序开发者的设计任务,通过实现动态定义、装载和评估。对于开发人员,集成一种或多种脚本语言的任务是简单的,从越来越长的可选列表中选出一个确实困难的。本文描述了一些伴随java应用中脚本语言支持的问题,并从不同角度比较了Groovy, JudoScript, Pnuts, JRuby, Jacl, Jython, Rhino和Beanshell,以期能帮助读者作出正确的决定。
三年前,我在javaworld写了一遍叫做“javascripting语言,那种是适合你的?”的文章。当我收集解释器并进行比较时,我尽量选择那些看起来满足苛刻商务需求的。理想状态下,我希望解释器能够方便的扩展应用的用户接口,并且有易读的脚本代码,高可靠,快速,有好的支持和文档,并且是完备的。在那个时候,我把列表限制到了Jacl,Jython,Rhion和BeanShell。
过去的三年中发生了很多变化。可选项不再是很少的几个,不管是动态开发还是直接选择,可选的脚本语言都有一打以上。可靠的选择列表比三年前增多了,现在还包括了Groovy, JudoScript, Pnuts和BeanShell。我们还可以考虑不再这个列表中的其他解释器,但这个列表中,已经足够开发人员自己的所需了。
我预备标准化所有解释器,看看Jacl, Jython, Rhion和BeanShell在2002年后性能有没有提高,并看看Groovy, JudoScript, Jruby和Pnuts同它们比较起来会怎样。我认为,看看不同脚本语言有什么独特之处,有什么非凡的强项和弱点是很有意思的事情。
商务风险
在以前的文章里,我讲述了一些闻名的优秀脚本解释器的资料,并说明了结合脚本解释器时你可能碰到的风险。本文中,我把这些内容简化为一些要点,并根据我在写那些文章之后的经验进行了改进。Java脚本解释器的优点是毋庸置疑的。使用脚本语言编码比使用java简单;脚步语言使程序的应用逻辑和用户界面的推动(drive/驱动?)和扩展成为可能;脚本代码可以违反java应用中类接口而运行,这是非常强大的功能。这样可以轻易的编写程序测试驱动(write test drivers against your program),与编码并编译用于java类的单元测试相比,这是更加快速的。另外,假如用户花时间使用脚本扩展你的应用,他们就作你的工具上进行了投资,这使得你在竞争中多了一件利器。
但是,当在应用中集成jiava脚本解释器时,你必须面对一定的风险。两个主要的风险是,解释器可能成为孤儿,或者是当你把产品装上后,你可能发现解释器的致命缺陷。大多数解释器是通过开源模型动态维护和更新的,在这种情况下,你可以向研究你所发现问题的专家寻求帮助,给解释器打补丁,或者在未来版本中包含你需要的bug-fix(bug修理?)。这是一种安全的赌博,但并不能得到足够保证。假如你正严厉的考虑采用某个特定的解释器,请先看看它的开发站点,看看它的代码的进化,看看上面的流言板,用户的提问都有答案否。这可以帮助你了解代码支持的实际情况。
自我保护的另一格措施是,对你预备采用的任脚本何解释器进行完全测试。一些解释器在发布时包含了一个单元测试集。在测试你的应用中集成的解释器时,这些单元测试可以作为你的更大的测试集中的一部分。在测试解释器和应用之间的集成时,可以剔出自己的工作(you have your work cut out for you),因为脚本解释器有足够的弹性,并向开发人员暴露了足够的功能。你在早期向质量保证投入时间,而不是在应用已经成为产品,当用户需要急切的bug修复时才考虑。
新的竞争者列表
假如你正在寻找一个脚本解释器,你有很多选择。一些解释器支持已经存在的语言,比如Ruby, Python, javascript, Java和Tcl。另外一些解释器,如JudoScript, Groovy和Pnuts,选择了它们自己的类似java的语言语法。在比较不同的解释器,需要进行的最大的选择是,那种脚本语言的语法能很好的适合你的应用。像这种个人偏好发生作用的技术选择,可能在不同的开发人员团队引起激烈的争论。也许本文能有助于解决一些争论。
我收集比较了最近发布的八种不同的脚本解释器。解释器及其版本都在下表中列出。假如你对这些解释器并不熟悉,我还给出了每种解释器功能和开发活动的概要(a thumbnail sketch)。
脚本语言
版本号
简短描述
Jacl
1.3.1
Tcl解释器的java实现。假如你希望在脚本中使用工具包来创建用户接口类,看看Swank工程中的包裹(wrap)java swing 工具的类集。Jacl已经存在较长时间了,并且还在持续改进。
Jython
2.1
Python解释器的java实现。我注重到的一个问题是,已经有很长一段时间没有看到这个解释器的新版本了。但在Jython的网站上,说明了改变这种现状的计划,并且有基金支持。
Rhino
1.6.1
javascript解释器的java实现。它还支持把脚本编译成类文件。它的最新版本在几个月前发布,并加入了XML支持。
JRuby
0.8
Ruby解释器的java实现。它正在发展中,其测试版0.8表现良好。
BeanShell
2.0 beta 2
它是一个java源文件解释器,正在持续的发展和加入新特性。2.0版本提供了完全的普通java源文件解释支持。
Groovy
1.0 beta 9
Groovy是把Python和Ruby的特征加入java类似语法形成的,由很多令人兴奋的特征。可以把脚本直接编译成类文件,对不同的IDE,又很多Groovy插件可供选择,JSR委员会正在制定Groovy的规范。
JudoScript
0.9
它有和javascript类似的编程语法,学习和使用更加轻易。在它的FAQ中提到了它的一个明确目标:“支持对象级,操作系统级和应用级的脚本”。我测试的0.9版运行良好。
Pnuts
1.1 beta 2
Pnuts有和java相似的编程语法,并保持持续更新。它可以把脚本直接编译成java类文件。
第一个指标:性能
未来测试第一项指标,我为每个解释器编写了等价的脚本代码,让他们完成简单的任务集并记录它们执行脚本所花的时间。我的测试脚本主要关注基本操作,如循环,整数比较和大的一维、二维数组分配和初始化。用于各个解释器的测试脚本和运行它们的java程序可以在原文资源部分下载。
在基准测试中最有用的信息是,解释器完成简单任务速度的apples-to-apples比较。假如你重点考虑吞吐量,基准数将十分重要。对每种脚本语言,我尽力编写相识的测试代码。测试使用Java1.4.2在东芝Tecra8100笔记本上运行,CPU为PIII700MHz,内存256MB。启动JVM时使用了默认的堆尺寸。出于向你展示解释器到底有多快还是多慢的爱好,我编写了测试用例的java代码,并在java1.4.2上运行了。测试集包括:
1到1,000,000的计数
1,000,000次整数相等比较
分配并初始化包含100,000个元素的数组
分配并初始化一个500*500的二维数组
在2002年后有提高么?
在告诉你哪个解释器最快之前,我们先看看图1,这个条状图列出了很多耗时任务的结果:1百万次整数相等比较。对2002年文章中讲述的4种脚本解释器,我给出了在Java1.3.1JVM和Java1.4.2上运行所需的时间。非常有趣的是,测试Jython时用的是同一个版本的脚本解释器,结果表明新版JVM上速度提升了25%。在加上我使用了和前一次测试完全相同的硬件,所以可以肯定JVM1.4.2减少了运行基础测试所需的时间。现在看看Rhino,BeanShell和Jacl发生了什么:新版Rhino在1.4.2的JVM上比在1.3.1的JVM上运行的旧版本快乐86%,Jacl的这个数字是76%。可以看出,性能提高了很多。
四项任务的总时间
由于解释器在速度方面都十分相似(至少对我的基准测试是这样),我把各解释器完成四项基准测试所耗的总时间算出来并在图2中给出。
多变的标志
对这些简单测试,Rhino,Pnuts和Jytho始终是最快的,紧跟在后面的时Groovy,然后是JudoScript,然后是其他的。这些性能参数对你是否有用,取决于你希望脚本语言做的事情。假如你的脚本函数包含大量的迭代,并且用户要等待结果,你就需要关注速度最快的解释器,或者你就该考虑用java实现高性能要求的算法,而不是脚本代码。假如你的脚本只需要很少的重复操作,这些解释器速度的差异就不是那么重要了,快速的硬件也会使问题变得不同。
还有一点需要指出的是,即使最快的解释器,完成上面测试所用的时间也是同样功能java代码所用时间的大约40倍。假如速度是你最主要的问题,你必须清楚,最有意义的事情是用java代替脚本代码实现要害算法。
一些脚本解释器支持脚本代码直接编译成字节码。我对这将会产生多大的性能差异非常好奇,所以我进行了另一项测试。我用Rhino脚本解释器把基准测试脚本编译成了字节码,然后我把整个基准测试集用脚本和脚本产生的字节码分别运行了10遍。令人惊异的是,与直接运行脚本相比,脚本码编译成字节码再运行仅仅节省了10%的时间。我最初任务,JVM的魔咒占用了运行测试集的大部分时间,但进一步的检查发现JVM魔咒本身只占测试集运行总时间的20%。简单脚本代码编译成字节码看起来会有积极的意义,但这并不一定是显著提高性能的银弹。也许在更长的或者,更加计算中心的脚本中,会看到不同的结果。
第二个标准:集成难度
第三个标准:许