【问题标题】:Access variable of ScriptContext using Nashorn JavaScript Engine (Java 8)使用 Nashorn JavaScript 引擎 (Java 8) 访问 ScriptContext 变量
【发布时间】:2017-07-09 08:37:07
【问题描述】:

我在 Java 中将以下代码与 Rhino JavaScript 引擎一起使用:

@Test
public void testRhino() throws ScriptException {
    final ScriptEngineManager factory = new ScriptEngineManager();
    final ScriptEngine engine = factory.getEngineByName("rhino");
    final String raw = "I am the raw value injected";
    final ScriptContext ctx = new SimpleScriptContext();
    ctx.setAttribute("raw", raw, ScriptContext.ENGINE_SCOPE);

    String script = "var result = 'I am a result';";
    script += "java.lang.System.out.println(raw);";
    script += "'I am a returned value';";

    final Object res = engine.eval(script, ctx);
    System.out.println(ctx.getAttribute("result"));
    System.out.println(res);
}

脚本的输出(使用Rhino)是:

I am the raw value injected
I am a result
I am a returned value

Nashorn JavaScript 引擎中,result 没有任何价值:

@Test
public void testNashorn() throws ScriptException {
    final ScriptEngineManager factory = new ScriptEngineManager();
    final ScriptEngine engine = factory.getEngineByName("nashorn");
    final String raw = "I am the raw value injected";
    final ScriptContext ctx = new SimpleScriptContext();
    ctx.setAttribute("raw", raw, ScriptContext.ENGINE_SCOPE);

    String script = "var result = 'I am a result';";
    script += "java.lang.System.out.println(raw);";
    script += "'I am a returned value';";

    final Object res = engine.eval(script, ctx);
    System.out.println(ctx.getAttribute("result"));
    System.out.println(res);
}

返回

I am the raw value injected
null
I am a returned value

如何使用nashorn 引擎访问ScriptContextresult 变量的值?

【问题讨论】:

    标签: javascript java rhino nashorn


    【解决方案1】:

    如果您使用ScriptEngine.createEngine API 创建 ENGINE_SCOPE 绑定,它将按预期工作:

    import javax.script.*;
    
    public class Main {
      public static void main(String[] args) throws Exception {
    
        final ScriptEngineManager factory = new ScriptEngineManager();
        final ScriptEngine engine = factory.getEngineByName("nashorn");
        final String raw = "I am the raw value injected";
        final ScriptContext ctx = new SimpleScriptContext();
    
        // **This is the inserted line**
        ctx.setBindings(engine.createBindings(), ScriptContext.ENGINE_SCOPE);
    
        ctx.setAttribute("raw", raw, ScriptContext.ENGINE_SCOPE);
    
        String script = "var result = 'I am a result';";
        script += "java.lang.System.out.println(raw);";
        script += "'I am a returned value';";
    
        final Object res = engine.eval(script, ctx);
        System.out.println(ctx.getAttribute("result"));
        System.out.println(res);
     }
    }
    

    【讨论】:

    • NashornScriptEngine#createBindings() 如果设置了 global-per-engine,则返回 new SimpleBindings(),如果未设置,则返回 createGlobalMirror()。使用-Dnashorn.args=--global-per-engine 运行,或在创建引擎之前添加行System.setProperty("nashorn.args", "--global-per-engine");,都将导致ctx.getAttribute("result") 再次返回null。解决 Nashorn 的全局问题是将 Nashorn 嵌入 Java 应用程序的最大痛苦之一。
    【解决方案2】:

    Nashorn 将存储在ScriptContext 中的Bindings 视为“只读”。 任何设置存储在Bindings 对象中的变量(或创建新变量)的尝试都将导致在nashorn.global 中创建一个新变量,该变量会以该名称隐藏Bindings 参数。

    您可以使用引擎来“评估”变量,使用以下代码:

    System.out.println( engine.eval("result", ctx) );
    

    然而,这很丑陋。 “结果”首先被编译成一个脚本,然后对该脚本进行评估,以返回变量的值。适合测试,但对于通用解决方案来说效率可能有点低。

    一种更好但可能更脆弱的方法是提取“nashorn.global”变量,并查询它以获得所需的值。

    Bindings nashorn_global = (Bindings) ctx.getAttribute("nashorn.global");
    System.out.println( nashorn_global.get("result") );
    

    另请参阅我在 Capturing Nashorn's Global Variables 中的 hack/answer,了解在评估脚本后将 nashorn.global 值移回 Map<String,Object> 的自动方式。

    【讨论】:

    • 可以使用 ScriptEngine.createBindings API 创建一个新的 Bindings 对象。这将创建一个直接由 Nashorn 的 Global 实例支持的 Binding - 它始终反映对 JS 全局范围的读/写。
    • @A.Sundararajan 如果设置了-Dnashorn.args=--global-per-engine,那么即使您使用ScriptEngine.createBindings(),问题仍然存在。
    • 使用 --globals-per-engine,每个引擎只有一个底层 JS/Nashorn 全局对象。所有 ENGINE_SCOPE 绑定将共享相同的底层 Nashorn 全局实例!正如您在上面观察到的,在 --global-per-engine 案例中的 engine.createBindings() 只会返回一个 SimpleBindings(而不是由新的 Nashorn Global 实例支持的 Bindings)。
    猜你喜欢
    • 2019-01-22
    • 1970-01-01
    • 2018-09-19
    • 2015-03-03
    • 2018-11-30
    • 1970-01-01
    • 2017-01-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多