首页 文章

Nashorn访问非静态Java方法

提问于
浏览
2

在Java 7(1.7)中,我可以通过运行以下命令从JavaScript访问Java方法:

ScriptEngine jse = new ScriptEngineManager().getEngineByName("JavaScript");
jse.eval("importClass(net.apocalypselabs.symat.Functions);");
jse.eval("SyMAT_Functions = new net.apocalypselabs.symat.Functions();");

String input = "notify(\"Foo\");"; // This is user input

jse.eval("with(SyMAT_Functions){ "+input+" }");

哪个将从Functions java类运行notify()函数:

public class Functions {
    private Object someObjectThatCannotBeStatic;
    public void notify(Object message) {
        JOptionPane.showMessageDialog(null, message.toString());
    }
    /* Lots more functions in here, several working with the same non-static variable */
}

如何使用Nashorn引擎访问Java 1.8中的Functions类?如果用户使用Java 1.8,我的目标是为第一个代码段运行不同的代码,同时仍允许1.7的用户使用该应用程序 .

我没试过http://www.doublecloud.org/2014/04/java-8-new-features-nashorn-javascript-engine/https://docs.oracle.com/javase/8/docs/technotes/guides/scripting/nashorn/api.htmlHow to instantiate a Java class in JavaScript using Nashorn? . 他们似乎都没有像Java 1.7那样允许我,而是假设我只想访问静态函数和对象 .

我得到的最常见的错误:

我从...开始

ScriptEngine jse = new ScriptEngineManager().getEngineByName("JavaScript");
jse.eval("var SyMAT_Functions;with (new JavaImporter(Packages.net.apocalypselabs.symat)) {"
                    + "SyMAT_Functions = new Functions();}");

...然后...

jse.eval("with(SyMAT_Functions){ "+input+" }");

...吐出......

TypeError: Cannot apply "with" to non script object in <eval> at line number 1

2 回答

  • 2

    我能够重现 . 首先,Nashorn一般不会尝试使用Java对象(非静态或其他方式) . 我已经在其他项目中使用它,并且没有任何重大问题从Java 7中的Rhino转换到migration guide中涵盖的内容 . 然而,根据MDN,这里的问题似乎是处理with statement的使用,这是"not recommended",甚至在ECMAScript 5.1的 strict 模式中也是不允许的 .

    与此同时,我发现a thread on the Nashorn-dev mailing list讨论了一个类似的案例 . 答复的相关部分是:

    Nashorn只允许脚本对象(即由JS构造函数或JS对象文字表达式创建的对象)作为“with”语句的作用域表达式 . 任意对象 . . . 不能用作'with'的'范围'表达式 . 在jdk9中,添加了支持以支持镜像其他脚本引擎或其他全局变量(它们是ScriptObjectMirror的实例)的脚本对象 .

    这不是最优雅的解决方案,但是,在不使用JDK 9的情况下,通过在Javascript中编写代理对象来镜像Java类的 public API,我能够实现 with 的预期用途:

    package com.example;
    
    import javax.script.*;
    
    public class StackOverflow27120811
    {
        public static void main(String... args) throws Exception {
            ScriptEngine jse = new ScriptEngineManager().getEngineByName("JavaScript");
            jse.eval(
                "var real = new Packages.com.example.StackOverflow27120811(); " +
                "var proxy = { doSomething: function(str) { return real.doSomething(str); } }; "
            );
            jse.eval("with (proxy) { doSomething(\"hello, world\"); } ");
        }
    
        public void doSomething(String foo) {
            System.out.println(foo);
        }
    }
    

    Attila Szegedi指出了非标准的Nashorn Object.bindProperties函数 . 虽然不能期望除了Nashorn引擎之外的任何东西,但它确实消除了重新声明 proxy 对象内所有公共API的复杂性 . 使用这种方法,第一个 jse.eval(...) 调用可以替换为:

    jse.eval(
        "var real = new Packages.com.example.StackOverflow27120811(); " +
        "var proxy = { }; " +
        "Object.bindProperties(proxy, real); " // Nashorn-only feature
    );
    
  • 0

    我决定使用我的应用程序编译并捆绑“旧”Rhino解释器,而不是使用Nashorn .

    https://wiki.openjdk.java.net/display/Nashorn/Using+Rhino+JSR-223+engine+with+JDK8

相关问题