我有一个Java应用程序,它依赖于ruby脚本的结果。我面临的问题是这个ruby脚本被多次调用,需要在每次执行时加载脚本库。
我不熟悉Ruby,但在我的搜索中,我遇到了JRuby。因为它解释了ruby代码并在JVM上运行它,我最初的想法是很好,我可以将ruby脚本编译成.class文件打包并与常规java对象进行交互"。这样可以避免每次执行ruby脚本时加载库所带来的开销。
在深入了解jruby之后,我理解它并没有这样做。所以要做我想做的事情,我可以使用JRuby的JavaEmbedUtils
这就是我的想法(我即将进行测试)
// 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次运行。
答案 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