[英]Java Scripting With Nashorn (JSR 223) & Pre-compilation
我通過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
,我總是不得不重新編譯大型庫代碼。
有沒有辦法用新代碼加入以前編譯的代碼片段?
將已編譯的函數放入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));
}
這是JSR-223的設計; eval(String)
后面沒有真正的代碼緩存。 嗯, 理論上它可以,但它體現了開發人員想要的很多猜測(並且所有猜測,在某些時候它肯定是錯誤的)。
你應該做的是評估你的Invocable
一次,保持它並重復使用它。
這樣做時,請注意Nashorn不提供線程安全性(JavaScript沒有線程概念,因此Nashorn故意不是線程安全的,以便在語言語義不強制時不必支付同步成本)。 因此,就基礎腳本中全局變量的狀態而言,創建的Invocable
在多個線程中使用是不安全的。 (同時運行不與腳本的全局狀態交互的函數很好。)
如果你需要跨線程共享它, 並且函數依賴於全局狀態, 並且全局狀態可以改變,那么你需要為它添加自己的腳手架(同步或資源池,或者其他任何當前的方式)以此目的)。
如果您需要預先編譯並使用各種參數調用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
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.