Jruby和ruby互操作[转]

1. 引言
2004年Ruby on Rails的横空出世让大家为之一惊,很多Java社区对它也投去关注的目光,现在RoR已经渐渐为人接受,被运用于不少实际项目之中,这也让本来不怎么吸引眼球的Ruby从角落里走了出来。不少开发者在试用了Ruby和RoR后产生了浓厚的兴趣,毕竟Ruby的语法是如此的有趣,Rails中的开发是如此的便捷,有时它替你安排好了一切,敲键盘就是了。
但Ruby毕竟和主流的Java/.Net还存在一定距离,比如开发者数量,受关注度等等。更关键的是它缺乏像Java那样的库支持,很多时候不得不自己动手“丰衣足食”。后来人们想到了要去跨越语言的边界,但做总是比想要难,好在出现了JRuby,在它的帮助下,这条边界已经不再不可逾越,所以勇敢地跨出第一步吧!

2. JRuby的Java集成
如何让Ruby与Java紧密地结合在一起呢?你可以在Ruby中引用Java类、Java原子类型、Java数组,实现Java接口,继承Java类;也可以在Java中使用Ruby的代码。其实一切都很简单,你需要的只是一点小小的魔法而已。

2.1. JRuby中调用Java
在接触JRuby前我使用过RJB(Ruby Java Bridge,http://rjb.rubyforge.org/),两者都提供在Ruby中调用Java的功能,仅在这点上来说,感觉它们差不多,其实JRuby的功能要强大的多。如果你只是想在Ruby中简单地调用一些Java代码,那可以考虑RJB。
要在JRuby中使用Java,先要声明程序中需要Java集成,有两种方法,一种用require 'java';另一种直接使用java::java.util.ArrayList这样的语法。无论是何种方法,都要保证所用的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"你可以这样来调用System.out.println:


java.lang.System.out.println("Hello, world")

java.lang.System.out.println("Hello, world")值得一提的是这里的”Hello, world”是Ruby的字符串,而非java.lang.String,JRuby会自动对一些类型进行转换,开发者无需自己动手。
在Java中,方法和变量都用fooBar这样的形式,而Ruby中则是foo_bar,显然在代码中同时出现这两种形式会很不协调,JRuby很聪明地将Java中的fooBar转为了foo_bar,而常见的getter和setter,也简化为了成员属性的名称,foo是getFoo(),而foo=是setFoo()。
用to_java()能将一个Ruby的数组转换为Java中的Object[],如果想要指定数组的类型可以这样:


[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)。

如要直接创建并使用Java数组,像下面这样就行了:

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. 扩展Java
对Java的扩展主要是用Ruby来实现接口和继承类。先来看下如何实现接口:


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如果要实现多个接口,import就可以了,对于未实现的方法,JRuby会把它交给method_missing。有一点要注意,compareTo在这里不能写成compare_to。
至于继承Java类就更容易了,几乎和继承Ruby类没什么区别:


class MyStringBuffer < java.lang.StringBuffer   
def append(v)
end
end

class MyStringBuffer < java.lang.StringBuffer
def append(v)
end

endStringBuffer类的append方法有多个overload的版本,接收多个不同类型的参数,它们都会被统一到这个唯一的方法上,理由嘛很好理解,不是吗?
除此之外,JRuby还为Java的集合类提供了很多扩展,让你能用Ruby的方式来操作Java集合。比方说,java.util.Map多了each方法、 []方法和[]=方法;java.lang.Comparable拥有了<=>方法;所有继承自java.util.Collection的类有了each、<<、+、-和length方法;java.util.List有了[]和[]=方法,还实现了sort和sort!方法。

2.3. Java中调用JRuby
Java 6中加入了JSR223,让Java可以支持脚本语言,如果你的运气没这么好,还停留在Java 5或者Java 1.4上,那可以考虑用BSF或者是直接用JRuby Runtime。当然,除非情况特殊,否则不推荐使用Runtime。
JSR223和BSF的用法比较相近,所以这里只演示一下JSR223。先去下载一个JSR223引擎包,把其中的JRuby引擎放进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项目的部署
既然是RoR的项目,自然是可以借鉴已有的最佳实践,JavaEye上对此已有很多讨论。不过目前还不能在JRuby on Rails中使用FastCGI,所以像JavaEye用的LightTPD+FastCGI就只能被暂时忽略了,等到FastCGI什么时候被JRuby on Rails支持了再让它重见天日吧。
既然用JRuby而非Ruby,那自然是有一定原因的,不是想在系统中使用Java资源,就是开发者有浓厚的Java情结。既然是JRuby on Rails,就让我们来看下Java开发者会比较喜欢的部署方式。

3.1. Java EE Web容器中的部署
如果RoR的项目跑在Tomcat里,那会是种什么感觉?如果Ruby文件全变成class了又会怎么样?这可不是睁眼说瞎话,在JRuby on Rails里你就能这么做!
GoldSpike能够把整个Rails应用程序打包为一个War文件,有了War就能在任意Java EE Web容器中进行直接的部署。
以插件形式安装好GoldSpike后,可以在Rails项目中用它提供的Rake任务来生成War文件。GoldSpike的配置都做在config/war.rb文件中,用如下命令开始构建,运行后会生成一个与项目同名的War文件:

jruby -S rake war:standalone:create  

jruby -S rake war:standalone:create下面来介绍些war.rb配置时的DSL:

写道
exclude_files FILENAME 用来指定不想被放入War的文件,可以使用通配符。
servlet CLASSNAME 分派Rails请求的类,默认是org.jruby.webapp.RailsServlet。
compile_ruby BOOLEAN 打包前编译所有的Ruby文件,目前这个功能似乎还不是很理想,所以默认是false。记得Robbin以前曾发过一篇文章说突然发现XRuby做的事情很有前途,JRuby同样能够做到,其实我不在乎用什么,只要把我的Ruby代码编程字节码就行。
add_gem NAME, VERSION 你需要手动添加程序用到的Gem包,好在有add_gem_dependencies,把它设为true(默认就是true),GoldSpike会自动添加依赖的包的。


add_gem 'RedCloth', '= 3.0.4'   

add_gem 'RedCloth', '= 3.0.4'

datasource_jndi BOOLEAN 如果在程序中使用了JNDI提供数据源,那将这个参数设置为true,并用datasource_jndi_name NAME来提供JNDI名称,JRuby on Rails中可以用ActiveRecord-JDBC来访问数据库,其中能够使用JNDI。
maven_library GROUP, NAME, VERSION 项目中如果需要Jar库,GoldSpike可以直接从Maven库中下载文件。


maven_library 'mysql', 'mysql-connector-java', '5.0.4' 


maven_library 'mysql', 'mysql-connector-java', '5.0.4' GoldSpike是JRuby-extras的一部分,欲了解相关信息,请访问https://rubyforge.org/projects/jruby-extras/ ,其中还有ActiveRecord-JDBC等信息。此外,由Nick Sieger开发的Warbler也是一个不错的选择。

3.2. Mongrel集群
在RoR中常会启动一组Mongrel来处理请求,JRuby on Rails中同样可以这么做,只是在做法上有所不同,因为直接启动几个Mongrel实例的同时会起好几个JVM,启动速度慢不说,还很耗资源,所以JRuby提供了一种机制,在同一个JVM中启动几个JRuby Runtime来运行程序。我们可以利用这种机制在一个JVM中启动几个Mongrel监听连续端口。这里会用到mongrel_jcluster,建议在用Gem安装Mongrel时就一起把这个mongrel_jcluster装了,你总是会用到的。
配置、启动及停止的命令如下:

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第一条命令会创建一个配置文件,启动4个Mongrel实例,端口从4000开始,JRuby通过20202端口来监听发送的命令(JRuby自己起了个服务器,接收命令,在一个JVM里运行),-K是服务器用的密钥。
至于放在Mongrel前处理静态资源及负载均衡的服务器,请自行查阅网上其他资源。

4. 总结
本文简单地介绍了JRuby中Ruby与Java的互操作和JRuby on Rails项目的部署问题,希望能够起到一个抛砖引玉的作用,让大家更多地关注JRuby,虽然它还有这样那样的问题,但也不失为一个好的选择。
JRuby目前的最大目标是与Ruby兼容,所以大量的精力都放在处理兼容性上,相信以后在性能上会有所提高,其实JRuby的速度已经慢慢接近C Ruby了。再者,JRuby背后有Sun的支持,NetBeans IDE 6.0中默认带了JRuby支持,GlassFish V3也将对它有所支持,这不都暗示JRuby将有所作为吗?
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值