Jruby和ruby互操作[轉(zhuǎn)]1. 引言
2004年Ruby on Rails的橫空出世讓大家為之一驚,很多Java社區(qū)對(duì)它也投去關(guān)注的目光,現(xiàn)在RoR已經(jīng)漸漸為人接受,被運(yùn)用于不少實(shí)際項(xiàng)目之中,這也讓本來(lái)不怎么吸引眼球的Ruby從角落里走了出來(lái)。不少開(kāi)發(fā)者在試用了Ruby和RoR后產(chǎn)生了濃厚的興趣,畢竟Ruby的語(yǔ)法是如此的有趣,Rails中的開(kāi)發(fā)是如此的便捷,有時(shí)它替你安排好了一切,敲鍵盤就是了。 但Ruby畢竟和主流的Java/.Net還存在一定距離,比如開(kāi)發(fā)者數(shù)量,受關(guān)注度等等。更關(guān)鍵的是它缺乏像Java那樣的庫(kù)支持,很多時(shí)候不得不自己動(dòng)手“豐衣足食”。后來(lái)人們想到了要去跨越語(yǔ)言的邊界,但做總是比想要難,好在出現(xiàn)了JRuby,在它的幫助下,這條邊界已經(jīng)不再不可逾越,所以勇敢地跨出第一步吧! 2. JRuby的Java集成 如何讓Ruby與Java緊密地結(jié)合在一起呢?你可以在Ruby中引用Java類、Java原子類型、Java數(shù)組,實(shí)現(xiàn)Java接口,繼承Java類;也可以在Java中使用Ruby的代碼。其實(shí)一切都很簡(jiǎn)單,你需要的只是一點(diǎn)小小的魔法而已。 2.1. JRuby中調(diào)用Java 在接觸JRuby前我使用過(guò)RJB(Ruby Java Bridge,http://rjb./),兩者都提供在Ruby中調(diào)用Java的功能,僅在這點(diǎn)上來(lái)說(shuō),感覺(jué)它們差不多,其實(shí)JRuby的功能要強(qiáng)大的多。如果你只是想在Ruby中簡(jiǎn)單地調(diào)用一些Java代碼,那可以考慮RJB。 要在JRuby中使用Java,先要聲明程序中需要Java集成,有兩種方法,一種用require 'java';另一種直接使用java::java.util.ArrayList這樣的語(yǔ)法。無(wú)論是何種方法,都要保證所用的Java類在CLASSPATH中。
require 'java' java::java.util.ArrayList require 'java' java::java.util.ArrayListJava類的使用也有幾種選擇:
include_class "java.util.HashMap"
x = HashMap.new
x.put("foo","bar")
include_class("java.lang.String") {|pkg,name| "JString"}
y = JString.new "Hello, world"
include_class "java.util.HashMap"
x = HashMap.new
x.put("foo","bar")
include_class("java.lang.String") {|pkg,name| "JString"}
y = JString.new "Hello, world"如果類是在java、javax、org或者com包中的,那還可以直接引用它們。 JString = java.lang.String y = JString.new "Hello, world" JString = java.lang.String y = JString.new "Hello, world"你可以這樣來(lái)調(diào)用System.out.println: java.lang.System.out.println("Hello, world") java.lang.System.out.println("Hello, world")值得一提的是這里的”Hello, world”是Ruby的字符串,而非java.lang.String,JRuby會(huì)自動(dòng)對(duì)一些類型進(jìn)行轉(zhuǎn)換,開(kāi)發(fā)者無(wú)需自己動(dòng)手。 在Java中,方法和變量都用fooBar這樣的形式,而Ruby中則是foo_bar,顯然在代碼中同時(shí)出現(xiàn)這兩種形式會(huì)很不協(xié)調(diào),JRuby很聰明地將Java中的fooBar轉(zhuǎn)為了foo_bar,而常見(jiàn)的getter和setter,也簡(jiǎn)化為了成員屬性的名稱,foo是getFoo(),而foo=是setFoo()。 用to_java()能將一個(gè)Ruby的數(shù)組轉(zhuǎn)換為Java中的Object[],如果想要指定數(shù)組的類型可以這樣:
[1,2,3].to_java :float # new float[] {1,2,3}
["str", "str2"].to_java java.lang.String # new String[]{"str","str2"}
[1,2,3].to_java :float # new float[] {1,2,3}
["str", "str2"].to_java java.lang.String # new String[]{"str","str2"}
常用的symbol有以下幾種::boolean、:byte、:char、:double、:float、:int、:long、:short、:object、:string、:big_decimal(:decimal)和:big_integer(:big_int)。 如要直接創(chuàng)建并使用Java數(shù)組,像下面這樣就行了:
java.lang.String[3].new java.lang.String[].new [3,3] java.lang.String[3][3].new d = java.lang.String[3,3].new d[0][0] = "Hello" d[0][1] = "World" java.lang.String[3].new java.lang.String[].new [3,3] java.lang.String[3][3].new d = java.lang.String[3,3].new d[0][0] = "Hello" d[0][1] = "World"2.2. 擴(kuò)展Java 對(duì)Java的擴(kuò)展主要是用Ruby來(lái)實(shí)現(xiàn)接口和繼承類。先來(lái)看下如何實(shí)現(xiàn)接口:
class Compare import java.lang.Comparable def compareTo o this <=> o end end class Compare import java.lang.Comparable def compareTo o this <=> o end end如果要實(shí)現(xiàn)多個(gè)接口,import就可以了,對(duì)于未實(shí)現(xiàn)的方法,JRuby會(huì)把它交給method_missing。有一點(diǎn)要注意,compareTo在這里不能寫成compare_to。 至于繼承Java類就更容易了,幾乎和繼承Ruby類沒(méi)什么區(qū)別:
class MyStringBuffer < java.lang.StringBuffer def append(v) end end class MyStringBuffer < java.lang.StringBuffer def append(v) end endStringBuffer類的append方法有多個(gè)overload的版本,接收多個(gè)不同類型的參數(shù),它們都會(huì)被統(tǒng)一到這個(gè)唯一的方法上,理由嘛很好理解,不是嗎? 除此之外,JRuby還為Java的集合類提供了很多擴(kuò)展,讓你能用Ruby的方式來(lái)操作Java集合。比方說(shuō),java.util.Map多了each方法、 []方法和[]=方法;java.lang.Comparable擁有了<=>方法;所有繼承自java.util.Collection的類有了each、<<、+、-和length方法;java.util.List有了[]和[]=方法,還實(shí)現(xiàn)了sort和sort!方法。 2.3. Java中調(diào)用JRuby Java 6中加入了JSR223,讓Java可以支持腳本語(yǔ)言,如果你的運(yùn)氣沒(méi)這么好,還停留在Java 5或者Java 1.4上,那可以考慮用BSF或者是直接用JRuby Runtime。當(dāng)然,除非情況特殊,否則不推薦使用Runtime。 JSR223和BSF的用法比較相近,所以這里只演示一下JSR223。先去下載一個(gè)JSR223引擎包,把其中的JRuby引擎放進(jìn)CLASSPATH。代碼如下:
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
public class JRubyJSR223 {
public static void main(String[] args) throws Exception {
ScriptEngineManager m = new ScriptEngineManager();
ScriptEngine rubyEngine = m.getEngineByName("jruby");
rubyEngine.getContext().setAttribute("num", new Integer(4), ScriptContext.ENGINE_SCOPE);
rubyEngine.eval("puts 2 + $num ");
}
}
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
public class JRubyJSR223 {
public static void main(String[] args) throws Exception {
ScriptEngineManager m = new ScriptEngineManager();
ScriptEngine rubyEngine = m.getEngineByName("jruby");
rubyEngine.getContext().setAttribute("num", new Integer(4), ScriptContext.ENGINE_SCOPE);
rubyEngine.eval("puts 2 + $num ");
}
}3. JRuby on Rails項(xiàng)目的部署 既然是RoR的項(xiàng)目,自然是可以借鑒已有的最佳實(shí)踐,JavaEye上對(duì)此已有很多討論。不過(guò)目前還不能在JRuby on Rails中使用FastCGI,所以像JavaEye用的LightTPD+FastCGI就只能被暫時(shí)忽略了,等到FastCGI什么時(shí)候被JRuby on Rails支持了再讓它重見(jiàn)天日吧。 既然用JRuby而非Ruby,那自然是有一定原因的,不是想在系統(tǒng)中使用Java資源,就是開(kāi)發(fā)者有濃厚的Java情結(jié)。既然是JRuby on Rails,就讓我們來(lái)看下Java開(kāi)發(fā)者會(huì)比較喜歡的部署方式。 3.1. Java EE Web容器中的部署 如果RoR的項(xiàng)目跑在Tomcat里,那會(huì)是種什么感覺(jué)?如果Ruby文件全變成class了又會(huì)怎么樣?這可不是睜眼說(shuō)瞎話,在JRuby on Rails里你就能這么做! GoldSpike能夠把整個(gè)Rails應(yīng)用程序打包為一個(gè)War文件,有了War就能在任意Java EE Web容器中進(jìn)行直接的部署。 以插件形式安裝好GoldSpike后,可以在Rails項(xiàng)目中用它提供的Rake任務(wù)來(lái)生成War文件。GoldSpike的配置都做在config/war.rb文件中,用如下命令開(kāi)始構(gòu)建,運(yùn)行后會(huì)生成一個(gè)與項(xiàng)目同名的War文件:
jruby -S rake war:standalone:create jruby -S rake war:standalone:create下面來(lái)介紹些war.rb配置時(shí)的DSL: 寫道 exclude_files FILENAME 用來(lái)指定不想被放入War的文件,可以使用通配符。 servlet CLASSNAME 分派Rails請(qǐng)求的類,默認(rèn)是org.jruby.webapp.RailsServlet。 compile_ruby BOOLEAN 打包前編譯所有的Ruby文件,目前這個(gè)功能似乎還不是很理想,所以默認(rèn)是false。記得Robbin以前曾發(fā)過(guò)一篇文章說(shuō)突然發(fā)現(xiàn)XRuby做的事情很有前途,JRuby同樣能夠做到,其實(shí)我不在乎用什么,只要把我的Ruby代碼編程字節(jié)碼就行。 add_gem NAME, VERSION 你需要手動(dòng)添加程序用到的Gem包,好在有add_gem_dependencies,把它設(shè)為true(默認(rèn)就是true),GoldSpike會(huì)自動(dòng)添加依賴的包的。
add_gem 'RedCloth', '= 3.0.4' add_gem 'RedCloth', '= 3.0.4' datasource_jndi BOOLEAN 如果在程序中使用了JNDI提供數(shù)據(jù)源,那將這個(gè)參數(shù)設(shè)置為true,并用datasource_jndi_name NAME來(lái)提供JNDI名稱,JRuby on Rails中可以用ActiveRecord-JDBC來(lái)訪問(wèn)數(shù)據(jù)庫(kù),其中能夠使用JNDI。 maven_library GROUP, NAME, VERSION 項(xiàng)目中如果需要Jar庫(kù),GoldSpike可以直接從Maven庫(kù)中下載文件。
maven_library 'mysql', 'mysql-connector-java', '5.0.4' maven_library 'mysql', 'mysql-connector-java', '5.0.4' GoldSpike是JRuby-extras的一部分,欲了解相關(guān)信息,請(qǐng)?jiān)L問(wèn)https:///projects/jruby-extras/ ,其中還有ActiveRecord-JDBC等信息。此外,由Nick Sieger開(kāi)發(fā)的Warbler也是一個(gè)不錯(cuò)的選擇。 3.2. Mongrel集群 在RoR中常會(huì)啟動(dòng)一組Mongrel來(lái)處理請(qǐng)求,JRuby on Rails中同樣可以這么做,只是在做法上有所不同,因?yàn)橹苯訂?dòng)幾個(gè)Mongrel實(shí)例的同時(shí)會(huì)起好幾個(gè)JVM,啟動(dòng)速度慢不說(shuō),還很耗資源,所以JRuby提供了一種機(jī)制,在同一個(gè)JVM中啟動(dòng)幾個(gè)JRuby Runtime來(lái)運(yùn)行程序。我們可以利用這種機(jī)制在一個(gè)JVM中啟動(dòng)幾個(gè)Mongrel監(jiān)聽(tīng)連續(xù)端口。這里會(huì)用到mongrel_jcluster,建議在用Gem安裝Mongrel時(shí)就一起把這個(gè)mongrel_jcluster裝了,你總是會(huì)用到的。 配置、啟動(dòng)及停止的命令如下:
jruby -S mongrel_rails jcluster::configure -e production -p 4000 -c . -N 4 -R 20202 -K yourVerySecretKey jruby -S mongrel_rails jcluster::start jruby -S mongrel_rails jcluster::stop jruby -S mongrel_rails jcluster::configure -e production -p 4000 -c . -N 4 -R 20202 -K yourVerySecretKey jruby -S mongrel_rails jcluster::start jruby -S mongrel_rails jcluster::stop第一條命令會(huì)創(chuàng)建一個(gè)配置文件,啟動(dòng)4個(gè)Mongrel實(shí)例,端口從4000開(kāi)始,JRuby通過(guò)20202端口來(lái)監(jiān)聽(tīng)發(fā)送的命令(JRuby自己起了個(gè)服務(wù)器,接收命令,在一個(gè)JVM里運(yùn)行),-K是服務(wù)器用的密鑰。 至于放在Mongrel前處理靜態(tài)資源及負(fù)載均衡的服務(wù)器,請(qǐng)自行查閱網(wǎng)上其他資源。 4. 總結(jié) 本文簡(jiǎn)單地介紹了JRuby中Ruby與Java的互操作和JRuby on Rails項(xiàng)目的部署問(wèn)題,希望能夠起到一個(gè)拋磚引玉的作用,讓大家更多地關(guān)注JRuby,雖然它還有這樣那樣的問(wèn)題,但也不失為一個(gè)好的選擇。 JRuby目前的最大目標(biāo)是與Ruby兼容,所以大量的精力都放在處理兼容性上,相信以后在性能上會(huì)有所提高,其實(shí)JRuby的速度已經(jīng)慢慢接近C Ruby了。再者,JRuby背后有Sun的支持,NetBeans IDE 6.0中默認(rèn)帶了JRuby支持,GlassFish V3也將對(duì)它有所支持,這不都暗示JRuby將有所作為嗎? |
|
|