最近开始流行区分Java平台和Java语言,但很多Java开发者还是不能确定如何在 Java应用程序开发中结合脚本。本篇文章,Gregor Roth给出了在Java平台上使用脚本的方法。通过这篇文章,你可以了解怎样在你的Java应用程序中使用脚本,是否你要通过使用Groovy和 Jython把不同的Java应用程序模块粘合在一起,或者写一个你自己的基于JRuby的应用程序,适用于Java平台。 作为一个Java开发者,你可能已经注意到了,Java语言已经不是Java平台的唯一所有者。Groovy也是一种适用于Java平台的编程语 言,而且还有很多其他语言的解释器和编译器,都可以运行于Java虚拟机(JVM)之上,其中最流行的就是Jython和JRuby。另外,Java SE 6内置对于脚本引擎的支持,JSR 223定义了一个标准接口,与运行于Java平台的动态语言进行交互。 为了能在Java平台上做脚本开发,你需要清楚地理解所要面临的挑战和获得的好处。你需要理解从JRuby和 Jython写成的脚本中调用Java类时会发生什么事情,你同样需要知道要集成一个脚本语言到Java平台会遇到的困难,在你的开发过程中会产生什么样 的影响。最后,你应该知道至少一些流行的脚本语言的不同特征,这样你才能知道他们适合放在你的程序的什么地方。 本篇文章概括了一些脚本语言(如Groovy,Jruby,Jython)和Java语言的区别,论述了一些在Java平台上使用脚本将会面临的挑战。同时,本文介绍了一些方法,使你可以集成一些最初由Ruby或Python写成的脚本到你的Java代码中。 静态类型VS动态类型 大部分脚本语言都是动态类型语言,而Java是一种静态类型语言。静态类型语言在声明变量的时候需要定义变量的类型,而且变量的值也只能设置成给定 的类型的值。与之相反,动态类型语言不需要程序员给出清楚的类型定义,因为类型信息附属于变量值,而不是变量。通过下面的列表1可以看到,Groovy定 义的变量V1可以被赋予任意不同类型的值。 列表1:Groovy动态类型
def v1 = '66' // sets the variable with a value of type String println v1.dump() // prints out: // <java.lang.String@6c0 value=[6, 6] offset=0 count=2 hash=1728> v1 = 9 // resets the variable with a value of type Integer println v1.dump() // prints out: // <java.lang.Integer@9 value=9> |
动态类型脚本语言的一种典型的实现方法,就是一直保持变量值和它的类型的可变性标签。当在操作中使用任何变量之前,才立即进行类型检查。这种做法的 缺点就是在运行时需要额外的处理循环来确定变量值的类型和进行类型检查。但静态类型的提倡者指出,使用动态类型语言总是导致性能不足。 弱类型 VS 强类型 一些流行的脚本语言都是弱类型。弱类型语言允许在不同的类型上进行操作。弱类型语言支持隐式类型转换或多态。例如列表2所示,Groovy允许在一个整型和字符串型上进行加操作。 列表2:Groovy弱类型的例子
def v1 = '66' // set v1 with a String typed value def v2 = 5 // set v2 with a Integer typed value def v3 = v1 + v2 println v3 // prints out: // 665 |
与Groovy一样,Python也是一个动态类型语言。但与Groovy相比,Python却是一个强类型语言,所以它不支持上述操作。强类型没有弱类型那么宽松,不允许不同类型之间的混合操作,见列表3。 列表3:Python强类型的例子
v1 = '66' # set v1 with a String typed value v2 = 5 # set v2 with a Integer typed value v3 = v1 + v2 # prints out: # TypeError: cannot concatenate 'str' and 'int' objects |
静态,强类型的Java语言,在声明时,对于对象类型有最强可能性的约束。例如,如果想用Java实现一个回调模式,需要先写一个回调接口,定义回调函数,声明返回值类型,以及所有参数和定义异常类型。具体的回调实现将通过使用这个接口进行引用和调用,见列表4。 列表4:Java回调模式的实现
// the call back handler definition interface IDataHandler { public boolean onData(INonBlockingConnection nbc) throws IOException; } // the server receives data and performs the handler's call back method class MultithreadedServer { private volatile boolean isRunning = true; private IDataHandler dataHandler = null; // accepts only an object which implements the IDataHandler interface MultithreadedServer(IDataHandler dataHandler) { this.dataHandler = dataHandler; } public void run() { while (isRunning) { // waiting for data // ... // ... and dispatch it dataHandler.onData(nbc); } } } // the call back implementation class SmtpProtocolHandler implements IDataHandler { public boolean onData(INonBlockingConnection nbc) throws IOException { SmtpSession session = (SmtpSession) nbc.getAttachment(); //... } } MultithreadedServer server = new MultithreadedServer(new SmtpProtocolHandler()); server.run(); |
#p# 它走起路来是否像一个鸭子? 当使用如Rython,Ruby或Groovy这样的脚本语言的时候,不需要定义像上面列表4定义的那样的接口。变量可以拥有任意对象类型的引用。 当发送一个消息给目标对象的时候,语言的runtime检查是否存在一个匹配的方法,然后调用这个方法;否则,就抛出一个异常。目标对象不需要实现特殊的 接口或是继承类。如果方法本身就存在,runtime就会自动调用它。这种行为被称作“duck typing”,(鸭子测试)意思就是“如果它看起来像一个鸭子,而且像鸭子一样呷呷地叫,那么它一定是一个鸭子。” 列表5:用Groovy写的回调处理
// the call back implementation (which doesn't implement any interface) class SmtpProtocolHandler { def onData(nbc) { def session = nbc.attachment //... } } def server = new MultithreadedServer(new SmtpProtocolHandler()) server.run() |
质量和性能 比较上面列表4和列表5的两段handler代码,很显然地发现,脚本语言代码比Java代码更紧凑,更易读。这主要是因为使用Groovy和 JRuby,你就不需要像使用Java(静态,强类型语言)那样写出所有的类型声明。但在另一方面,缺失类型信息也存在一些缺点。静态类型的支持者称:静 态,强类型语言可以通过在编译时检测类型错误来保证程序的健壮性;动态语言的支持者称:相反,像测试驱动开发这样的开发技术抵消了编译时检查的优点。 一般而言,脚本语言比系统程序语言运行的慢,但是运行时性能并不是要考虑的唯一问题。问题始终是,对于目标底层结构来说,是否执行起来足够快。除了 速度需求,其它像可靠性和可变性之类的质量问题也需要考虑。例如,在整个软件生命周期中,维护费用占总体开销的比例最大。减少维护费用的关键就是提高可靠 性和简易性。脚本语言比较简单,在这些方面通常就比像C++或Java这样的系统程序语言有更好的效果。 在Java平台上使用脚本的方法 现在,你不需要在使用Java和像Groovy,Ruby或Python这样的脚本语言之间做选择了。你的应用程序可以同时享用脚本语言的效率、简洁和Java平台的可靠性。在Java平台上使用脚本语言的关键是,要知道在哪儿用脚本语言最好,在哪儿用Java代码最好。 过去,脚本语言被看作是与Java组件连接在一起的一个细小的粘合代码层。现在,很多Java开发者在Java平台上使用脚本语言,完成很多工作, 他们只是依靠Java类库来提供那些不被他们所使用的脚本支持的特征(例如,Java平台提供很多企业层的特征,这些特征大部分脚本语言环境都不支持,如 事务管理,remoting通信,或是监测。) 不管你怎样使用它,脚本语言和Java平台的无缝粘合产生了一个更富足的开发环境,在这个环境下,你可以为恰当的任务选择恰当的语言。 有两种方法可以使你认识到什么是Java平台上的“polyglot”开发环境(完整开发环境):你可以或者在Java虚拟机上运行你自己的脚本,或者使用Java本身的接口/进程间通信,来在一个本身的脚本环境内执行脚本语言。 大多数流行的脚本语言的runtimes都是基于C/C++的。使用JNI,可以把你的Java环境连接到本身的脚本环境。对于Ruby,你可以使 用一个基于JNI的解决方案(如RJB),或者一个基于进程间的解决方案(如YAJB)。遗憾的是,很多解决方案都会有一些不合需求的限制。例如,基于进 程间的解决方案使用一个远程协议连接到当前环境,这可能会导致性能瓶颈。 基于Java的脚本runtime (动态语言支撑的结构)实现 开源工程,如Jython或JRuby被认为是纯粹的基于Java的脚本runtime实现,使你能够在Java虚拟机上执行Python或 Ruby脚本。这意味着,使用Python或Ruby写成的脚本可以运行在拥有Java SE runtime的所有平台上。基于Java的脚本rutimes只是会首先把脚本语言结合到Java平台里,并不提供你所想象的那些功能。 因为最初的Ruby或Python脚本的runtimes比基于Java的快很多,使用本身的runtime会是处理这些脚本的首选。而JRuby 和Jython这些语言的真正的价值是它们可以调用Java类和被Java类调用。这就把整个Java世界开放到了脚本语言。脚本语言可以访问任何由 Java实现的事情。 从底层结构来看,JRuby或Jython可以被看作是基于Java的脚本runtimes,来在Java平台上执行正规的Ruby或Python 脚本。从开发者角度看,JRuby或Jython可以被看作是丰富的脚本,它们使用Java类,需要格外的脚本runtime性能。因此,这些“J 脚本”不能执行在本身的脚本runtime上。 集成脚本到Java中 为了在Java虚拟机上运行Python,Ruby或Groovy,需要在Java classpath里添加基于Java的脚本runtimes的jars。设置完后,脚本引擎被实例化,就可以在Java环境里执行脚本了。大多数情况 下,会为脚本执行提供简单的引擎类,如列表6所示: 列表6:为Ruby,Groovy,Python提供的Rutime引擎
// run a Ruby scripting (JRuby V1.1b1) Ruby runtime = Ruby.getDefaultInstance(); runtime.executeScript(scripting, filename); // run a Groovy scripting (Groovy V1.1) GroovyShell gs = new GroovyShell(); gs.evaluate(scripting); // run a Python scripting (jython V2.2) PythonInterpreter interp = new PythonInterpreter(); interp.exec(scripting) |
大多数脚本引擎允许为你的脚本捆绑Java环境的主机变量,还可以调用特殊的脚本函数。需要注意的是一些脚本引擎需要额外的设置才可以使用扩展特征。如想要调用gems,就需要设置系统属性(jruby.home和jruby.lib)。 随着Java SE 6的发布,一个标准的host脚本引擎的接口被定制成为Java runtime的一个主要部分。JSR 223:在Java平台上使用脚本中有标准化函数,如:脚本引擎发现,捆绑Java主机变量,和脚本符号。JSR 223接口需要一个JSR 223兼容的脚本实现。主要脚本语言的实现可以从脚本工程主页上下载到。 #p# 从脚本中调用Java类 在脚本中引用Java类需要在使用前引入(import)Java类。例如,JRuby定义了一个特殊语句“include Java”来访问Java runtime中内置的Java类。其他非绑定的类引用时需要加前缀“Java ::”,可以参看列表7,JRuby脚本的前几条语句激活了Java支持,定义了常量来代表SSLContext类和 BlockingConnection类的路径。 与JRuby相比,脚本runtimes如Jython使用现有的、本身的脚本语句来引入Java类。如Python模块,可以使用普通的 Python语句“import<packagename>”引入Java包。Jython还支持Python的不同形式的import语 句,如“from <packagename> import <class>”。Groovy,是明确地被设计成运行在Java平台的,它使用Java的import语法。 紧凑的语法意味着代码量的减少 脚本语言的语法要比Java的语法紧凑的多。如,不管是Groovy,Ruby还是Python,都不需要你写冗长的getters和 setters(属性设置)。为了简化JavaBeans属性操作,这些脚本语言的Java runtime允许用直接的、类似脚本的方式使用这些属性。 一个Java方法,如<object>.getDefault()可以被写成<object>.default。而 且,JRuby runtime会自动把Java的CamelCase(骆驼拼写法)的命名形式匹配为Ruby的形式。如,你可能会这样写一个Java方 法:<object>.activateSecureMode(),在JRuby中就变成 了<object>.activate_secured_node()。 基于这样的特征,Java类看起来就像是普通的脚本类,参看列表7。这个例子是在Jruby中使用Java的网络类库来实现一个SMTP客户端。 列表7:在JRuby中使用Java类库(实现一个SMTP客户端)
include Java # set up constants to shorten the paths SSLContext = javax.net.ssl.SSLContext BlockingConnection = Java::org.xsocket.stream.BlockingConnection #performs new BlockingConnection(String, int, SSLContext.getDefault(), boolean) bc = BlockingConnection.new('smtp.web.de', 25, SSLContext::default, false)
bc.receive_timeout_millis = 60 * 1000 # performs setReceiveTimeoutMillis(long) puts bc.read_string_by_delimiter("\r\n") # performs readStringByDelimiter(String) bc.write("STARTTLS\r\n") puts bc.read_string_by_delimiter("\r\n") bc.activate_secured_mode() bc.write('AUTH PLAIN ' + ["\000" + 'from.me' + "\000" + 'myPassword'].pack('m')) puts bc.read_string_by_delimiter("\r\n") bc.write("HELO Server\r\n") puts bc.read_string_by_delimiter("\r\n") bc.write("MAIL FROM: from.me@web.de\r\n") puts bc.read_string_by_delimiter("\r\n") bc.write("RCPT TO: itsyou@gmx.net\r\n") puts bc.read_string_by_delimiter("\r\n") bc.write("data\r\n") puts bc.read_string_by_delimiter("\r\n") mail = 'Message-ID: 46D957AF.2020804 Date: Sat, 01 Sep 2007 14:14:39 +0200 From: from.me@web.de User-Agent: my mail client MIME-Version: 1.0 To: itsyou@gmx.net Subject: what I have to say Content-Type: text/plain; charset=ISO-8859-15; format=flowed Content-Transfer-Encoding: 7bit combining scripting languages with Java is great' bc.write("#{mail}\r\n.\r\n") puts bc.read_string_by_delimiter("\r\n") bc.write("quit\r\n") puts bc.read_string_by_delimiter("\r\n") bc.close() |
在Java虚拟机上运行脚本语言可以让你无缝结合Java代码和脚本。通过调用一个嵌入的Java对象的方法,脚本runtime寻找Java类库 的一个恰当的方法特征。通过识别一个匹配的Java方法,runtime执行这个方法调用。针对脚本的参数的数据类型自动转化成适合的Java类型。 调用重载的Java方法 就像前面提到的,动态类型语言不需要类型声明。这样也存在不便之处,一个隐藏的陷阱就是调用重载的Java方法。当调用一个重载的Java方法的时候,脚本runtime需要选择合适的方法。某些情况下,选择的方法实现并不是所期望的,参看下面两个例子。 列表8给出了一个重载的Java类。 列表8:一个重载的Java类
package test; public class NonBlockingConnection { public void write(int i) { System.out.println("writing int " + i); } public void write(long l) { System.out.println("writing long " + l); } } |
列表9给出了Jython如何调用这些重载方法 列表9:使用Juthon调用重载方法
from java.lang import Integer from test import NonBlockingConnection # call 1 NonBlockingConnection().write(214748364700) # call method based on python built-in data type # prints out: # writing long 214748364700 # call 2 NonBlockingConnection().write(55) # call method based on python built-in data type # prints out: # writing long 55 # ups, not int? # call 3 NonBlockingConnection().write(Integer(55)) # pass over a Java object instead of python data type # prints out: # writing int 55 |
Jython脚本的Call 2部分并没有完成期望的int类型的Java方法的调用。一个用来确定调用哪个重载方法的比较实用的方法是,在脚本中实例化需要的Java数据类型的对 象,用它代替脚本内的类型。当你使用Groovy时,方法重载根本不是问题,因为Groovy同时支持静态和动态类型。 #p# 从Java调用脚本类 可以在Java环境中通过使用脚本引擎来执行脚本。这样的脚本引擎同时允许执行专门的脚本方法或是函数。JSR 223定义了一个标准接口“Invokable”来实现这样的方法和功能。可以参看列表10,Ruby脚本的一个专门的函数“write”将被调用。 列表10:调用Ruby脚本的一个专门函数
String rubyScript = "def write(msg) \r\n" + " puts msg \r\n" + "end \r\n" + "\r\n" + " def anotherFunction() \r\n" + " # do something \r\n" + "end \r\n"; ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName("jruby"); engine.eval(rubyScript);
Invocable inv = (Invocable) engine; inv.invokeFunction("write", new Object[] { "hello" }); // prints out: // hello |
一些脚本环境如Jython还支持一个编译器来产生Java类。这种情况下,一个Jython类可以像一个正常的Java类那样被调用。只是产生的Java类的自身能力是受限制的。 重写重载的Java方法 JRuby和Jython都支持实现Java接口和继承Java类。但是对于大多数流行的脚本语言来说,重写重载的Java方法就带来了一个新问 题。怎样定义哪个重载的Java方法应该重写?如Rython2.x或Ruby不支持基于类型特征的方法重载。这需要处理重写的方法的所有重载方法。方法 的实现需要对所有重载的方法做参数类型的检测。列表11给出了如何用Jython实现 列表11:使用Jython调用重载方法
from test import NonBlockingConnection from java.lang import Integer from java.lang import Long class ExtendedNonBlockingConnection(NonBlockingConnection): def write(self, *args): # the signature to handle? if len(args) == 1 and isinstance(args[0], Integer): print 'writing overriden int ' + args[0].toString() # no, let the super method do the stuff else: NonBlockingConnection.write(self, *args) ExtendedNonBlockingConnection().write(Integer(777)) # prints out: # writing overridden int 777 ExtendedNonBlockingConnection().write(Long(777)) # prints out: # writing long 777 |
因为Groovy还支持静态类型声明,重载的方法可以通过参数类型进行声明,如列表12所示。 列表12:使用Groovy调用重载方法
import test.NonBlockingConnection class ExtendedNonBlockingConnection extends NonBlockingConnection { // overwrite the int method def write(int i) { println 'writing overridden int ' + i } } new ExtendedNonBlockingConnection().write((int) 777) // prints out: // writing overridden int 777 new ExtendedNonBlockingConnection().write((long) 777) // prints out: // writing long 777 |
一个双向的完整的应用程序(SMTP服务器) Java和脚本语言的紧密结合,通常需要通过脚本元素调用Java类,通过Java类调用脚本元素。因此,应用程序由基于脚本的类和基于Java的类这两部分无缝、双向地结合组成。 列表13给出了一个完整的基于脚本的应用程序,调用了Java类。它是一个用JRuby写的SMTP服务器,使用了一个非阻塞的Java网络类库。 列表13:JRuby写的SMTP服务器
include Java RandomAccessFile = java.io.RandomAccessFile DataConverter = Java::org.xsocket.DataConverter MultithreadedServer = Java::org.xsocket.stream.MultithreadedServer IConnection = Java::org.xsocket.stream.IConnection IConnectHandler = Java::org.xsocket.stream.IConnectHandler IDataHandler = Java::org.xsocket.stream.IDataHandler class TestMessageSinkManager def new_message_sink_channel() file = java.io.File.create_temp_file('smtptest', 'mail') return RandomAccessFile.new(file, 'rw').channel end end
class SmtpProtocolHandler include IConnectHandler include IDataHandler def initialize(domain, message_sink_manager) @domain = domain @msg_sink_mgr = message_sink_manager @helo_pattern = Regexp.compile(/HELO.*/, Regexp::IGNORECASE) @mail_from_pattern = Regexp.compile(/MAIL FROM:.*/, Regexp::IGNORECASE) @rcpt_to_pattern = Regexp.compile(/RCPT TO:.*/, Regexp::IGNORECASE) @data_pattern = Regexp.compile(/DATA.*/, Regexp::IGNORECASE) @quit_pattern = Regexp.compile(/QUIT.*/, Regexp::IGNORECASE) end
# new incoming (non blocking) connection def onConnect(nbc) nbc.flushmode = IConnection::FlushMode::ASYNC nbc.attachment = { 'state' => 'CMD', 'msg_num' => 0 } nbc.write("220 #{@domain} SMTP ready \r\n") return true end
# data received for the (non blocking) connection def onData(nbc)
case nbc.attachment['state'] # message receiving mode: non-blocking streaming of the msg data when 'MESSAGE' # some validations have to be performed by the data sink delimiter_found = nbc.read_available_by_delimiter("\r\n.\r\n", nbc.attachment['message_channel']) if delimiter_found nbc.attachment['message_channel'].close() nbc.attachment['state'] = 'CMD' nbc.write("250 OK #{nbc.get_id()}.#{nbc.attachment['msg_num']} \r\n") end # smtp-command mode: perform command else # a BufferUnderflowException will been thrown, if delimiter not found smtp_cmd_line = nbc.read_string_by_delimiter("\r\n")
case smtp_cmd_line when @helo_pattern nbc.write("250 #{@domain} SMTP Service \r\n")
when @mail_from_pattern originator = smtp_cmd_line[10,9999].strip() # ...here some validations should be performed (valid address, ...) nbc.attachment['originator'] = originator nbc.attachment['recipients'] = [] nbc.write("250 #{@originator} is syntactically correct\r\n") when @rcpt_to_pattern rec = smtp_cmd_line[8,9999].strip() # ...here some validations should be performed (max recipients, ...) nbc.attachment['recipients'] = nbc.attachment['recipients'] << rec nbc.write("250 #{rec} verified \r\n") when @data_pattern # ...here some validation should be performed (recipients set, ...) nbc.attachment['state'] = 'MESSAGE' nbc.attachment['msg_num'] = nbc.attachment['msg_num'] + 1 nbc.attachment['message_channel'] = @msg_sink_mgr.new_message_sink_channel() time_stamp = "Received: FROM #{nbc.remote_address.canonical_host_name} BY #{@domain}\r\n" + "id #{nbc.get_id()}.#{nbc.attachment['msg_num']}; " + Time.new.to_s() + "\r\n" nbc.attachment['message_channel'].write(DataConverter.to_byte_buffer(time_stamp, 'US-ASCII')) nbc.write("354 Enter message, ending with \".\" \r\n") when @quit_pattern nbc.write("221 SMTP service closing connection \r\n") nbc.close() else nbc.write("500 Unrecognized command \r\n")
end end return true end end server = MultithreadedServer.new(25, SmtpProtocolHandler.new('mSrv', TestMessageSinkManager.new)) server.run() |
在这个应用程序中,一个基于Java的服务器被实例化,监听将到来的SMTP网络连接。网络事件由基于JRuby的Handler处理。为了做这件事情,基于JRuby的Handler需要实现一个由Java网络类库定义的Java回调接口。 为了实现这个Java接口,JRuby类需要使用“include <java interface>”语句来声明支持所有接口。与基于Java的接口实现不同,返回类型或异常不需要由基于JRuby的方法实现定义。通过实现 handler的一个回调函数,一个Java对象被转换成(以InonBlockingConnection实例)JRuby脚本。 对这个Java对象的访问就会被脚本环境截获到。因此,它可以就像是一个Ruby对象那样,在JRuby方法实现内部处理。原始的数据类型,如Java的Integer或Long都被映射成相应的Ruby类型。 如果一个网络事件发生,服务器完成handler的恰当的回调方法。这个可以工作成功,因为基于Ruby的handler对服务器来说就像是一个规 则的Java类。JRuby runtime自动包装这个基于JRuby的handler,把它转化给Java服务器。Java服务器不需要获得本身的JRuby Handler,而是获得一个代理,支持所有由这个Handler实现的Java接口的方法。 总结 基于Java的脚本runtimes努力地把Java平台和你所选择的脚本语言结合在一起。在现在这个早期阶段,脚本runtime引擎有很大不 同。在当前的JRuby或Jython的版本中,你可能会发现一些Java平台所没有的新特征,如注释支持。这对于架起Java语言和脚本语言之间的语义 桥梁来说,同样是一种挑战,有些时候需要一些并不好看的解决方案。这就是说,大多数情况下,那些支持的特征对于使用Java平台,Java代码,脚本语言 来写一个企业层应用程序来说,也是足够了。 在列表13中提到的那个双向、完整的应用程序例子是一个非阻塞的,多线程的SMTP服务器,是使用脚本语言类和Java类共同写成的。使用一个现有 的Java网络类库处理低水平的、性能重要的、网络明确的任务,如线程或连接管理。控制任务通过脚本语言实现。显现出来的Java平台和脚本语言之间的协 和作用使得写出高性能、可升级的应用程序成为了一种可能。所面临的挑战是如何为恰当的任务选择恰当的语言。 在脚本方面,你可以选择现有的语言,如JRuby或Jython,和一个适用于运行在Java平台上的脚本语言,如Groovy。第一组使得 Java类看起来像是有规则的脚本。Groovy使用一个与Java代码很相似的语法,但是它更进化,每一个Groovy类是一个丰满的Java类。 Groovy与其他脚本语言相比,对Java开发者来说更简单,它可以完全无缝地使用Java类库,不需要任何中转。 想要学习更多的关于在Java平台上使用脚本的方法,或是了解“polyglot”编程,或是对这篇文章中提到的语言感兴趣,可以参看资源部分 作者简历 Gregor Roth是一个软件架构师,工作于United Internet组,这是一个欧洲Internet服务提供商。他所感兴趣的领域包括:软件和系统结构,企业结构管理,面向对象设计,分布式计算,开发方 法,当然还包括Java。他的职业生涯是开始于C和基于汇编的微型控制器应用软件。1997年,他开始设计和开发大型的、基于Java的分布式企业系统。 原文链接:http://www.javaworld.com/javaworld/jw-11-2007/jw-11-jsr223.html?page=1 |