我目前正在使用此java代码执行我的JavaScript脚本:
ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
engine.eval(new FileReader("awesome_script.js"));
我需要从JavaScript调用Java函数,所以我在awesome_script.js
文件的顶部定义了它:
var first = Java.type('io.github.awesomeprogram.FirstClass');
var second = Java.type('io.github.awesomeprogram.SecondClass');
var extra = Java.type('io.github.awesomeprogram.ExtraClass');
然后我可以从这些类中调用一些方法,例如:
second.coolmethod("arg1",2);
我现在的问题是我需要在脚本中使用很多java类。我也有很多脚本,我认为在每个脚本中定义这些类中的每一个都是非常低效的。
所以我正在寻找一种解决方案来创建在Java内部使用Java.type()
创建的对象,然后将它们传递给我想要执行的脚本。
我该怎么做?
提前致谢!
答案 0 :(得分:1)
您可能希望避免在“jdk.internal。”,“jdk.nashorn.internal。”等软件包中使用“内部”类。在jdk9中,dynalink是一个API(“jdk.dynalink”已导出包)。在jdk9中,您可以调用jdk.dyanlink.beans.StaticClass.forClass(Class)[http://download.java.net/java/jdk9/docs/jdk/api/dynalink/jdk/dynalink/beans/StaticClass.html#forClass-java.lang.Class-]来构造“类型”对象,并将它们作为全局变量公开给脚本引擎。对于jdk8,您可以在评估“用户”脚本之前预先使用Java.type(String)调用的脚本。您也可以从Java代码中调用“Java.type”函数。
jdk9的解决方案:
import jdk.dynalink.beans.StaticClass;
import javax.script.*;
public class Main {
public static void main(String[] args) throws Exception {
ScriptEngineManager m = new ScriptEngineManager();
ScriptEngine e = m.getEngineByName("nashorn");
e.put("AList", StaticClass.forClass(java.util.ArrayList.class));
e.eval("var al = new AList(); al.add('hello'), al.add('world')");
e.eval("print(al)");
}
}
jdk8的解决方案:
import javax.script.*;
public class Main {
public static void main(String[] args) throws Exception {
ScriptEngineManager m = new ScriptEngineManager();
ScriptEngine e = m.getEngineByName("nashorn");
// eval a "boot script" before evaluating user script
// Note that this script could come from your app resource URL
e.eval("var AList = Java.type('java.util.ArrayList')");
// now evaluate user script!
e.eval("var al = new AList(); al.add('hello'), al.add('world')");
e.eval("print(al)");
}
}
jdk8的替代解决方案:
import javax.script.*;
import jdk.nashorn.api.scripting.*;
public class Main {
public static void main(String[] args) throws Exception {
ScriptEngineManager m = new ScriptEngineManager();
ScriptEngine e = m.getEngineByName("nashorn");
// get Java.type function as object
JSObject javaTypeFunc = (JSObject) e.eval("Java.type");
// you can javaTypeFunc from java code many times
Object alType = javaTypeFunc.call(null, "java.util.ArrayList");
// expose that as global
e.put("AList", alType);
// now evaluate user script!
e.eval("var al = new AList(); al.add('hello'), al.add('world')");
e.eval("print(al)");
}
}
答案 1 :(得分:0)
经过大量研究后,我发现了一种在执行前将全局变量放入ScriptEngine的方法:The Java Scripting API (Oracle Docs)
这使我能够将我想要的任何对象放入全局变量中。但是,我仍然需要一种方法来获取Java.type()
在Java中创建的Object。所以我编写了一个测试脚本,它返回一个这样的对象,我发现它是一个jdk.internal.dynalink.beans.StaticClass
类型的对象。这个类有一个构造函数,它以普通的Class
作为参数。遗憾的是,这个构造函数在我的代码中不可用,因为它不可见。为了绕过这个,我使用了反射并制作了这个方法:
public StaticClass toNashornClass(Class<?> c) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{
Class<?> cl = Class.forName("jdk.internal.dynalink.beans.StaticClass");
Constructor<?> constructor = cl.getDeclaredConstructor(Class.class);
constructor.setAccessible(true);
StaticClass o = (StaticClass) constructor.newInstance(c);
return o;
}
如果我将我想要的对象的Class
作为全局变量传递,我只需要调用toNashornClass(Example.class);
并将生成的对象放入具有engine.put("example",object);
工作正常。我可以完全像example
创建的var一样使用Java.type()
var。