从Java调用Ruby方法

时间:2017-10-03 19:12:50

标签: java ruby jruby

我有一个Java应用程序,它依赖于ruby脚本的结果。我面临的问题是这个ruby脚本被多次调用,需要在每次执行时加载脚本库。

我不熟悉Ruby,但在我的搜索中,我遇到了JRuby。因为它解释了ruby代码并在JVM上运行它,我最初的想法是很好,我可以将ruby脚本编译成.class文件打包并与常规java对象进行交互"。这样可以避免每次执行ruby脚本时加载库所带来的开销。

在深入了解jruby之后,我理解它并没有这样做。所以要做我想做的事情,我可以使用JRuby的JavaEmbedUtils

  1. 使用我的ruby脚本加载Ruby运行时对象
  2. 创建一个基本上知道如何处理和响应我的脚本的接收器对象。
  3. 从脚本调用所需的方法。
  4. 这就是我的想法(我即将进行测试)

    // 1  
    List<String> paths = new ArrayList<>();  
    paths.add(".");  
    Ruby runtime = JavaEmbedUtils.initialize(paths);
    
    // 2  
    String script = "/path/to/script.rb";  
    IRubyObject recvr = JavaEmbedUtils.newRuntimeAdapter().eval(runtime, script);
    
    // 3 Call this many times   
    JavaEmbedUtils.invokeMethod(runtime, receiver, "method", null, null);
    

    我的理解是否正确,因为这种方法允许我在加载脚本库一次时多次使用脚本的内容?是否有替代或更多的JRuby方式来做我正在寻找的东西?

    更新

    所以我测试了类似于Eugene建议的东西(a),即ScriptingContainer,并将其与使用(b)java.lang.Runtime调用脚本进行比较,共计30次运行。

    1. 平均而言,(a)约快8倍(b)
    2. 初始化(a)的运行时间约为7秒
    3. (a)&amp;的初始运行(b)40x&amp;比以下运行时间长150-250倍。
    4. (a)的初始运行速度比(b)s
    5. 快11倍

1 个答案:

答案 0 :(得分:2)

这就是我在java代码的脚本容器中使用JRuby的方法:

import org.jruby.*;
import org.jruby.embed.LocalVariableBehavior;
import org.jruby.embed.PathType;
import org.jruby.embed.ScriptingContainer;

ScriptingContainer container = new ScriptingContainer(LocalVariableBehavior.PERSISTENT);
container.setCompileMode(RubyInstanceConfig.CompileMode.OFF);
container.setNativeEnabled(false);
container.setObjectSpaceEnabled(true);
container.put("some_param", someValue);

// My script return an array - tweak to fit your returning value
RubyArray resourceArray = (RubyArray) container.runScriptlet(PathType.CLASSPATH, scriptPath);

我必须注意JRuby在发布时太慢了(这些数字不太精确,但在我的情况下它是2-3秒。甚至在4Ghz + SSD上)。 TDD因此而陷入痛苦。这就是为什么有一些调整作为对象空间等等。

另外,我不得不将这个模块存根,所以其余的测试使用其他灯具并在不启动JRuby的情况下运行。换句话说,我不必在单独运行另一个测试时启动它。

P.S。

  

我的理解是否正确,因为这种方法要求我只加载脚本的依赖项一次?

我不确定你在这里是什么意思,但似乎每次使用这种方法时都会加载JRuby端所需的所有依赖项。

无论如何,我之前会做一个基准测试。也许不值得考虑。

<强> UPD:

这比我想象的更简单:

common.rb:

puts "common"

script.rb:

require 'common'

puts "script"

代码:

ScriptingContainer container = new ScriptingContainer(LocalVariableBehavior.PERSISTENT);
for (int i = 0; i < 3; i++) {
    container.runScriptlet(PathType.CLASSPATH, "script.rb");
}

输出:

common
script
script
script