使用Nashorn编写Java脚本(JSR 223)和预编译

我通过JSR 223使用Nashorn来执行用户输入脚本的小片段:

public Invocable buildInvocable(String script) throws ScriptException { ScriptEngine engine = new ScriptEngineManager().getEngineByName(ENGINE); engine.eval(functions); engine.eval(script); return (Invocable) engine; } 

变化的用户脚本调用在静态中央库中定义的JavaScript函数(保存在上面代码片段中的functions String中)。

每当我想要获取一个可以从我的Java调用的Invocable ,我就不得不重新编译大型库代码。

有没有办法用新代码加入以前编译的代码片段?

这是JSR-223的设计; eval(String)后面没有真正的代码缓存。 嗯, 理论上它可以,但它体现了开发人员想要的很多猜测(并且所有的推测,它在某些时候一定是错的)。

你应该做的是评估你的Invocable一次,保持它并重复使用它。

在这样做时,请注意Nashorn不提供线程安全性(JavaScript没有线程概念,因此Nashorn故意不是线程安全的,以便在语言语义不强制时不必支付同步成本)。 因此,就底层脚本中全局变量的状态而言,创建的Invocable在多个线程中使用是不安全的。 (同时运行不与脚本的全局状态交互的函数很好。)

如果你需要跨线程共享它, 并且函数依赖于全局状态, 并且全局状态可以改变,那么你需要为它添加自己的脚手架(同步或资源池,或者其他任何当前的方式)以此目的)。

将已编译的函数放入Bindings中,如:

 private static final String FUNCTIONS = "function() {" + " return \"Hello\";" + "}"; public static void main(String... args) throws Exception { ScriptEngine engine = new ScriptEngineManager().getEngineByMimeType("text/javascript"); // Compile common functions once CompiledScript compiled = ((Compilable) engine).compile(FUNCTIONS); Object sayHello = compiled.eval(); // Load users' script each time SimpleBindings global = new SimpleBindings(); global.put("sayHello", sayHello); String script = "sayHello()"; System.out.println(engine.eval(script, global)); } 

如果您需要预编译并使用各种参数调用JavaSctipt函数,您可以单独编译它们并在Java中汇编执行流程。 使用Java8中的JavaScript引擎Nashorn,您可以:

 private static final String FUNCTIONS = "function hello( arg ) {" + //<-- passing java.lang.String from Java " return 'Hello ' + arg;" + //<-- returning string back "};" + "function sayTime( arg ) {" + //<-- passing java.util.HashMap from Java " return 'Java time ' + arg.get( 'time' );" + //<-- returning string back "};" + "function () {" + //<-- this callable "function pointer" is being returned on [step1] below " return { 'hello': hello, 'sayTime': sayTime };" + "};"; public static void main(String... args) throws Exception { ScriptEngine engine = new ScriptEngineManager().getEngineByName( "Nashorn" ); CompiledScript compiled = ((Compilable) engine).compile(FUNCTIONS); ScriptObjectMirror lastFunction = (ScriptObjectMirror)compiled.eval(); // [step1] ScriptObjectMirror functionTable = (ScriptObjectMirror)lastFunction.call( null ); // this method retrieves function table String[] functionNames = functionTable.getOwnKeys( true ); System.out.println( "Function names: " + Arrays.toString( functionNames ) ); System.out.println( functionTable.callMember( "hello", "Robert" ) ); //<-- calling hello() with String as argiment Map map = new HashMap(); map.put( "time", new Date().toString() ); //<-- preparing hashmap System.out.println( functionTable.callMember( "sayTime", map ) ); //<-- calling sayTime() with HashMap as argument } 

您可以在JavaSctipt中传递Java对象,请参阅上面的java.util.HashMap示例。

输出是:

 Function names: [hello, sayTime] Hello Robert Java time Fri Jan 12 12:23:15 EST 2018