Java代码无法使用新上下文从scriptengine调用方法

时间:2016-09-22 16:39:33

标签: java scriptengine

我正在尝试用Java中的Javascript实现示例调用方法。

private static final String JS = "function doit(p) { list.add(p); return true; }";

public static void main(String[] args) throws ScriptException, NoSuchMethodException {
    List<String> list = new ArrayList<>();


    ScriptEngineManager scriptManager = new ScriptEngineManager();
    ScriptEngine engine = scriptManager.getEngineByName("nashorn");

    ScriptContext context = new SimpleScriptContext();
    context.setBindings(engine.createBindings(), ScriptContext.ENGINE_SCOPE);
    Bindings scope = context.getBindings(ScriptContext.ENGINE_SCOPE);

    scope.put("list", list);
    engine.eval(JS, context);

    Invocable invocable = (Invocable) engine;
    invocable.invokeFunction("doit", "Hello!!!");

    System.out.println(list.size());
}
}

此代码抛出异常:

Exception in thread "main" java.lang.NoSuchMethodException: No such function doit
    at jdk.nashorn.api.scripting.ScriptObjectMirror.callMember(ScriptObjectMirror.java:204)
    at jdk.nashorn.api.scripting.NashornScriptEngine.invokeImpl(NashornScriptEngine.java:383)
    at jdk.nashorn.api.scripting.NashornScriptEngine.invokeFunction(NashornScriptEngine.java:190)
    at testjavascriptinteraction.TestJavaScript.main(TestJavaScript.java:32)

版本

java -version
openjdk version "1.8.0_91"
OpenJDK Runtime Environment (build 1.8.0_91-8u91-b14-3ubuntu1~16.04.1-b14)
OpenJDK 64-Bit Server VM (build 25.91-b14, mixed mode)

有什么问题?

2 个答案:

答案 0 :(得分:0)

我可以重现这个问题,并找到了解决问题的方法。

我有一种预感,在转换为Invocable之后,仍在使用引擎拥有的脚本上下文,而不是您传递给eval的临时脚本。

我可以通过在转换为engine.setContext(...)之前使用此上下文调用Invacable来解决此问题。即:

...
scope.put("list", list);
engine.eval(JS, context); // 'context' now contains the 'doit' function.

engine.setContext(context); // <-- Right here
Invocable invocable = (Invocable) engine; // Now the Invocable will use 'context'.
invocable.invokeFunction("doit", "Hello!!!"); //Runs fine

System.out.println(list.size());

这项工作的事实似乎证实了我的预感。

答案 1 :(得分:0)

您的scopeeval()都使用自定义ScriptContext,但invokeFunction()未使用。该功能在上下文中注册,而不是引擎。

选项1:不要使用自定义上下文。

List<String> list = new ArrayList<>();

ScriptEngineManager scriptManager = new ScriptEngineManager();
ScriptEngine engine = scriptManager.getEngineByName("nashorn");

Bindings scope = engine.getBindings(ScriptContext.ENGINE_SCOPE);
scope.put("list", list);

engine.eval("function doit(p) { list.add(p); return true; }");

Invocable invocable = (Invocable) engine;
invocable.invokeFunction("doit", "Hello!!!");

System.out.println(list); // prints: [Hello!!!]

<强>更新

选项2:直接调用注册函数。

函数作为带有函数值的正常变量在上下文中注册。这意味着scope调用后doit会有一个eval()变量。

您必须使用Nashorn类直接调用它,但这很容易。

// same code as above up to eval call
engine.eval("function doit(p) { list.add(p); return true; }", context);

// Instead of invokeFunction(), just get and call function manually
JSObject func = (JSObject)scope.get("doit");
func.call(null, "Hello!!!"); // like a "static" call, no 'this' value

System.out.println(list); // prints: [Hello!!!]