簡體   English   中英

使用Nashorn的Java腳本(JSR 223)和預編譯

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM