我有一个 Java 7 项目,它大量使用 Javascript 编写各种功能的脚本。到目前为止,我一直在使用 Rhino 作为脚本引擎。我现在想迁移到 Java 8,这也意味着我将用 Nashorn 取代 Rhino。
Nashorn 与 Rhino 的兼容性如何?我可以将它用作替代品,还是我可以期望我的某些脚本不再工作并且需要移植到新引擎?Rhino 有哪些常用的功能是 Nashorn 不支持的?
一个问题是 Nashorn 在默认情况下不能再通过使用将整个 Java 包导入全局范围importPackage(com.organization.project.package);
但是,有一个简单的解决方法:通过将此行添加到您的脚本中,您可以启用 Rhino 的旧行为:
load("nashorn:mozilla_compat.js");
我遇到的另一个问题是,在 java 和 javascript 之间传递数据时,某些类型转换的工作方式不同。例如,当您将 Javascript 数组传递给 Java 时到达的对象不能再转换为List
,但可以转换为Map<String, Object>
. 作为一种解决方法,您可以使用 Javascript 代码将 Javascript 数组转换为 Java 列表Java.to(array, Java.type("java.util.List"))
要在 JDK 8 上使用importClass方法,我们需要添加以下命令:
load("nashorn:mozilla_compat.js");
但是,此更改会影响 JDK 7 上的执行(JDK 不支持 load 方法)。
为了保持两个 SDK 的兼容性,我通过添加 try/catch 子句解决了这个问题:
try{
load("nashorn:mozilla_compat.js");
}catch(e){
}
当内部类被声明为私有时,Nashorn 无法访问该内部类,Rhino 能够做到这一点:
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
public class Test {
public static void main(String[] args) {
Test test = new Test();
test.run();
}
public void run() {
ScriptEngineManager factory = new ScriptEngineManager();
ScriptEngine engine = factory.getEngineByName("JavaScript");
Inner inner = new Inner();
engine.put("inner", inner);
try {
engine.eval("function run(inner){inner.foo(\"test\");} run(inner);");
} catch (ScriptException e) {
e.printStackTrace();
}
}
private class Inner {
public void foo(String msg) {
System.out.println(msg);
}
}
}
在 Java8 下,此代码会引发以下异常:
javax.script.ScriptException: TypeError: kz.test.Test$Inner@117cd4b has no such function "foo" in <eval> at line number 1
at jdk.nashorn.api.scripting.NashornScriptEngine.throwAsScriptException(NashornScriptEngine.java:564)
at jdk.nashorn.api.scripting.NashornScriptEngine.evalImpl(NashornScriptEngine.java:548)
我注意到 Rhino 对名为“in()”的函数没有问题(尽管“in”是 JavaScript 的保留关键字)。
然而,Nashorn 提出了一个错误。
Nashorn 不能在实例上调用静态方法!Rhino 这样做了,因此我们不得不将 Rhino 反向移植到 Java 8(这里有一个简短的总结:http ://andreas.haufler.info/2015/04/using-rhino-with-java-8.html )
Java8 上的 Nashorn 不支持 AST。因此,如果您有使用 Rhino 的 AST 机制检查 JS 源代码树的 Java 代码,则在移植代码以使用 Nashorn 后,您可能必须重写它(可能使用正则表达式)。
我说的是这个 API https://mozilla.github.io/rhino/javadoc/org/mozilla/javascript/ast/AstNode.html
Java9 上的 Nashorn 支持 AST。
Rhino 中而不是 Nashorn 中的一项功能:通过实例公开静态成员。
我的信念是,通过实例公开静态成员是一种草率地将原本独立的命名空间混合在一起,因此我选择不启用它。
我认为这是非常错误的。只要我们必须使用两种不同的构造来访问同一个 java 对象并在 javascript 中不必要地使用包声明,代码就会变得更难读写,因为认知负荷会增加。那我宁愿坚持Rhino。
我还没有找到解决这个明显的“设计错误”的方法。