我通过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
字符串中)。
每当我想获得一个我可以从Java调用的Invocable
时,我就不得不重新编译大型库代码。
有没有办法用新代码加入以前编译过的代码?
答案 0 :(得分:16)
将已编译的函数放入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));
}
答案 1 :(得分:10)
这是JSR-223的设计; eval(String)
背后无法真正拥有代码缓存。好吧,理论上它可以,但它体现了开发人员想要的很多猜测(并且所有猜测,它在某些时候肯定是错误的)。
您应该做的是评估您的Invocable
一次,保留它并反复使用它。
当这样做时,请注意Nashorn不提供线程安全性(JavaScript没有线程概念,因此Nashorn故意不是线程安全的,以便在语言语义不强制时不必支付同步成本) 。因此,就基础脚本中全局变量的状态而言,创建的Invocable
在多个线程中使用是不安全的。 (同时运行不与脚本的全局状态交互的函数很好。)
如果你需要在线程和之间共享它们的功能取决于全局状态,和全局状态可以改变,那么你需要添加自己的脚手架为此目的(同步,或资源池,或其他目前为此目的的方式)。
答案 2 :(得分:1)
如果您需要预先编译并使用各种参数调用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