论坛首页 编程语言技术论坛

当Ruby遇到Java,勇敢地跨越边界吧

浏览 21148 次
该帖已经被评为精华帖
作者 正文
   发表时间:2008-01-24  

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

Java类的使用也有几种选择:

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"

你可以这样来调用System.out.println:

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"}

常用的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"

2.2. 扩展Java
对Java的扩展主要是用Ruby来实现接口和继承类。先来看下如何实现接口:

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

StringBuffer类的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 ");
  }
}

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

下面来介绍些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' 

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'  

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

第一条命令会创建一个配置文件,启动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将有所作为吗?

   发表时间:2008-01-24  
Sorry, cant type Chinese on this machine.
   However, from InfoQ, a news said: some guys more encourage Sun company investing on groovy rather than jruby, they said the fundamental part on jruby is a mess ...
   I dont know if this is true, but groovy grows even faster than jruby these days.
0 请登录后投票
   发表时间:2008-01-24  
我也用过Groovy,它出来后没多久就变成JSR了,语法发生了些变化,它比起JRuby与Java的结合更紧密,其实各有个的好,Groovy也有Grails就看你怎么用了。
再说,Grovvy可以自顾自的发展,JRuby还要时刻注意与C Ruby兼容,保证Rails能跑在上面,兼容性比较重要,步伐慢点我觉得可以理解,Ruby 1.9刚发,好戏应该还在后面吧,呵呵~

P.S.
刚才去infoq看了下,你说的那篇是请愿:Sun,请停止支持JRuby吧,感觉把在JRuby上投资说成浪费时间有点偏激了~
0 请登录后投票
   发表时间:2008-01-24  
对 Jruby性能问题似乎遇到了瓶颈

我关注scala有一段时间了 过一阶段发个 测试的demo看看 也很不错 毕竟写出的东西没有好的测试去辅佐 谁放心呢?scala不差!
0 请登录后投票
   发表时间:2008-01-25  
neusun 写道
对 Jruby性能问题似乎遇到了瓶颈

正如我前面说的,JRuby首要关注的是兼容性,不过JRuby团队也在不断改善JRuby的性能。1月8号发布了JRuby 1.1RC1,介绍里有这么一段话:
引用
JRuby 1.1RC1 is the first release candidate of JRuby 1.1.  JRuby 1.1 represents a concerted focus on speed and refinement.  Ruby code can completely compile in an Ahead Of Time (AOT) or Just In Time (JIT) mode; yielding a faster Ruby!  It also uses less memory than our previous releases.

此外,JRuby可以把rb编译为class,官方站点中有篇关于JRuby Compiler的wiki,其中有一节是性能对比,内容如下:(估计用的还是JRuby 0.9.9)
引用
A few microbenchmarks comparing Ruby, JRuby interpreted, and JRuby compiled (server VM numbers show worst and best numbers):
fib(30) Ruby:                1.67s
fib(30) JRuby interp (client VM):    3.93s
fib(30) JRuby interp (server VM):    2.28s to 2.08s
fib(30) JRuby compiled (client VM):    1.89s to 1.79s
fib(30) JRuby compiled (server VM):    1.66s to 0.86s
0 请登录后投票
   发表时间:2008-01-25  
现在java的最主要的创新是JVM而非java语法本身,让各种编程语言跑在同一个平台(JVM)里面是一件非常美妙的事情.
0 请登录后投票
   发表时间:2008-01-26  
好,谢谢您,写得不错啊
0 请登录后投票
   发表时间:2008-01-26  
鸡肋,新瓶装旧酒,就像PHP也可以引用JAVA一样。
0 请登录后投票
   发表时间:2008-01-27  
hibernate vs ejb,jdo jruby vs groovy 本来没什么标准的,用的人多了就是事实标准 有的软件是根据需要进化而来,而有的是认为捏造的。。
0 请登录后投票
   发表时间:2008-01-27  
兼容带来的后果就是一团糟,发展路线不会很清晰,Jmess.
还是早日有个了断吧。
0 请登录后投票
论坛首页 编程语言技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics