Nashorn访问非静态Java方法

在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.html ,以及如何使用Nashorn在JavaScript中实例化Java类? 没有运气。 他们似乎都没有像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  at line number 1 

我能够重现。 首先,Nashorn一般不会尝试使用Java对象(非静态或其他方式)。 我已经在其他项目中使用过它,并且没有任何重大问题从Java 7中的Rhino转换到迁移指南中涵盖的内容。 然而,根据MDN,这里的问题似乎是处理使用“不推荐”的with语句 ,甚至在strict的ECMAScript 5.1模式下也是不允许的。

与此同时,我在Nashorn-dev邮件列表上找到了一个讨论类似案例的post 。 答复的相关部分是:

Nashorn只允许脚本对象(即由JS构造函数或JS对象文字表达式创建的对象)作为“with”语句的作用域表达式。 任意对象。 。 。 不能用作’with’的’范围’表达式。

在jdk9中,添加了支持以支持镜像其他脚本引擎或其他全局变量(它们是ScriptObjectMirror的实例)的脚本对象。

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

 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 ); 

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

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