Java使用ScriptEngine(javax.script)


import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.Bindings;
import javax.script.ScriptContext;
import javax.script.Invocable;
import javax.script.ScriptException;
復制代碼
        ScriptEngine engine = new ScriptEngineManager()
                .getEngineByName("javascript");
        
        // 建立上下文變量
        Bindings bind = engine.createBindings();
        
        // 加入變量champion值為GSP
        bind.put("champion", "GSP");

     // 綁定上下文,作用域為當前引擎范圍 engine.setBindings(bind, ScriptContext.ENGINE_SCOPE);
// 執行腳本(另外還有文件讀取方式 //如:engine.eval(new FileReader("xx.js"))); engine.eval("function sayName(){return champion;}"); // 可有調用方法 Invocable invoke = (Invocable) engine; // 調用方法,此處無參數列表 String result = invoke.invokeFunction("sayName", null).toString(); System.out.println(result);

1、可用的腳本引擎
  Java 6提供對執行腳本語言的支持,這個支持來自於JSR223規范,對應的包是javax.script。默認情況下,Java 6只支持javascript腳本,它底層的實現是Mozilla Rhino,它是個純Java的javascript實現。可以通過下面的代碼列出當前環境中支持的腳本引擎:
  1.ScriptEngineManager manager = new ScriptEngineManager();
  2.        ListScriptEngineFactory> factories = manager.getEngineFactories();
  3.        for (ScriptEngineFactory f : factories) {
  4.            System.out.println(
  5.                    "egine name:"+f.getEngineName()+
  6.                    ",engine version:"+f.getEngineVersion()+
  7.                    ",language name:"+f.getLanguageName()+
  8.                    ",language version:"+f.getLanguageVersion()+
  9.                    ",names:"+f.getNames()+
  10.                    ",mime:"+f.getMimeTypes()+
  11.                    ",extension:"+f.getExtensions());
  12.        }
  輸出結果:egine name:Mozilla Rhino,engine version:1.6 release 2,language name:ECMAScript,language version:1.6,names:[js, rhino, javascript, javascript, ECMAScript, ecmascript],mime:[application/javascript, application/ecmascript, text/javascript, text/ecmascript],extension:[js]。
  可以看到,Java內置只支持javascript一種腳本。但是,只要遵循 JSR223,便可以擴展支持多種腳本語言,可以從https://scripting.dev.java.net/上查找當前已被支持的腳本的第三方庫。
  2、hello script
  接下來給出在Java中使用javascript的Hello world示例:
  13.ScriptEngineManager manager = new ScriptEngineManager ();
  14.        ScriptEngine engine = manager.getEngineByName ("js");
  15.        String script = "print ('hello script')";
  16.        try {
  17.            engine.eval (script);
  18.        } catch (ScriptException e) {
  19.            e.printStackTrace();
  20.        }
  使用的API還是很簡單的,ScriptEngineManager是ScriptEngine的工廠,實例化該工廠的時候會加載可用的所有腳本引擎。從工廠中創建ScriptEngine可以使用getEngineByName、getEngineByExtension或 getEngineByMimeType來得到,只要參數名字能對上。執行腳本調用eval方法即可(效果等同於javascript中的eval)。
  3、傳遞變量
  可以向腳本中傳遞變量,使得Java代碼可以和腳本代碼交互,示例如下:
  21.ScriptEngineManager manager = new ScriptEngineManager();
  22.        ScriptEngine engine = manager.getEngineByName("js");
  23.        engine.put("a", 4);
  24.        engine.put("b", 6);
  25.        try {
  26.            Object maxNum = engine.eval("function max_num(a,b){return (a>b)?a:b;}max_num(a,b);");
  27.            System.out.println("max_num:" + maxNum);
  28.        } catch (Exception e) {
  29.            e.printStackTrace();
  30.        }
  輸出內容:max_num:6
  對於上面put的變量,它作用於自身engine范圍內,也就是ScriptContext.ENGINE_SCOPE,put 的變量放到一個叫Bindings的Map中,可以通過 engine.getBindings(ScriptContext.ENGINE_SCOPE).get(“a”);得到put的內容。和 ENGINE_SCOPE相對,還有個ScriptContext.GLOBAL_SCOPE 作用域,其作用的變量是由同一ScriptEngineFactory創建的所有ScriptEngine共享的全局作用域。
  4、動態調用
  上面的例子中定義了一個javascript函數max_num,可以通過Invocable接口來多次調用腳本庫中的函數,Invocable接口是 ScriptEngine可選實現的接口。下面是個使用示例:
  31.ScriptEngineManager manager = new ScriptEngineManager();
  32.        ScriptEngine engine = manager.getEngineByName("js");
  33.        try {
  34.            engine.eval("function max_num(a,b){return (a>b)?a:b;}");

35.            Invocable invoke = (Invocable) engine;
  36.            Object maxNum = invoke.invokeFunction("max_num",4,6);
  37.            System.out.println(maxNum);
  38.            maxNum = invoke.invokeFunction("max_num", 7,6);
  39.            System.out.println(maxNum);
  40.        } catch (Exception e) {
  41.            // TODO: handle exception
  42.        }
  上面的invokeFunction,第一個參數調用的腳本函數名,后面跟的可變參數是對應的腳本函數參數。
  Invocable還有個很酷的功能,就是動態實現接口,它可以從腳本引擎中得到Java Interface 的實例;也就是說,可以定義個一個Java接口,其實現是由腳本完成。以上面的例子為例,定義接口JSLib,該接口中的函數和javascript中的函數簽名保持一致:
  1.public interface JSLib {
  2.       public int max_num(int a,int b);
  3.   }
  調用示例:
  4.ScriptEngineManager manager = new ScriptEngineManager();
  5.        ScriptEngine engine = manager.getEngineByName("js");
  6.        try {
  7.            engine.eval("function max_num(a,b){return (a>b)?a:b;}");
  8.            Invocable invoke = (Invocable) engine;
  9.            JSLib jslib = invoke.getInterface(JSLib.class);
  10.            int maxNum = jslib.max_num(4,6);
  11.            System.out.println(maxNum);
  12.        } catch (Exception e) {
  13.            // TODO: handle exception
  14.        }
  5、使用Java對象
  可以在javascript中使用Java代碼,這確實是很酷的事情。在Rhino中,可以通過importClass導入一個類,也可以通過importPackage導入一個包,也可以直接使用全路經的類。在創建對象時,new也不是必須的。示例代碼如下:
  15.ScriptEngineManager manager = new ScriptEngineManager();
  16.        ScriptEngine engine = manager.getEngineByName("js");
  17.        try {
  18.            String script = "var list = java.util.ArrayList();list.add(\"kafka0102\");print(list.get(0));";
  19.            engine.eval(script);
  20.        } catch (Exception e) {
  21.            e.printStackTrace();
  22.        }
  6、編譯執行
  腳本引擎默認是解釋執行的,如果需要反復執行腳本,可以使用它的可選接口Compilable來編譯執行腳本,以獲得更好的性能,示例代碼如下:
  23.ScriptEngineManager manager = new ScriptEngineManager();
  24.        ScriptEngine engine = manager.getEngineByName("js");
  25.        try {
  26.            Compilable compEngine = (Compilable) engine;
  27.            CompiledScript script = compEngine.compile("function max_num(a,b){return (a>b)?a:b;}");
  28.            script.eval();
  29.            Invocable invoke = (Invocable) engine;
  30.            Object maxNum = invoke.invokeFunction("max_num",4,6);
  31.            System.out.println(maxNum);
  32.        } catch (Exception e) {
  33.            e.printStackTrace();
  34.        }
  7、總結
  除了上面提到的特性,腳本引擎還有一些不錯的功能,比如可以執行腳本文件,可以由多線程異步執行腳本等功能。引入腳本引擎,可以對一些配置擴展和業務規則做更強大而靈活的支持,也方便使用者選擇自己熟悉的腳本語言來編寫業務規則等

import java.io.FileReader;
  import java.util.ArrayList;
  import java.util.List;
  import javax.script.Bindings;
  import javax.script.Invocable;
  import javax.script.ScriptContext;
  import javax.script.ScriptEngine;
  import javax.script.ScriptEngineManager;
  import javax.swing.JFrame;
  /**
  *
  * @author hadeslee
  */
  publicclass Test {
  publicstaticvoid main(String[] args)throws Exception {
  //根據js的后綴名生成一個解析JS的腳本解析引擎

  ScriptEngine engin=new ScriptEngineManager().getEngineByExtension("js");
  //查詢一下這個引擎是否實現了下面很實用的接口

  System.out.println(engin instanceof Invocable);
  //聲明兩個對象,傳入到JS里面去

  JFrame jf=new JFrame("test");
  List list=new ArrayList();
  //得到挷定的鍵值對象,把當前的兩個JAVA對象放進去

  Bindings bind=engin.createBindings();
  bind.put("jf",jf);
  bind.put("list",list);
  //把挷下的鍵值對象放進去,作用域是當前引擎的范圍

  engin.setBindings(bind, ScriptContext.ENGINE_SCOPE);
  //用引擎執行一段寫在JS文件里面的代碼

  Object obj=engin.eval(new FileReader("test.js"));
  //這個時候返回值當然 是null了

  System.out.println(obj);
  //把當前的引擎強制轉為Invocable,這樣就可以調用定義在JS文件里面的一個一個函數了

  Invocable in=(Invocable)engin;
  //得到了從JS里面返回來的對象

  List l=(List)in.invokeFunction("getNames");
  System.out.println(l);
  //調用一下定義在JS里面的另一個函數

  in.invokeFunction("testJS");
  //最后調用一個函數,該函數可以使我們前面定義的窗體顯示出來

  in.invokeFunction("doSth");
  }
  }

  下面是定義在test.js里面的內容

  function doSth(){   jf.setSize(500,300);   jf.setVisible(true);   jf.setDefaultCloseOperation(jf.EXIT_ON_CLOSE);  }  function getNames(){   list.add("doSth");   list.add("getNames");   return list;   }  function testJS(){   print('Hello world!');   }

  我們可以看到,在JAVA運行了以后,窗體會顯示出來,並且我們可以接收到從JS解析引擎里面傳回的數據,當然我們也可以調用一個很普通的JS函數,想象一下,如果我們把我們程序運行時的一些對象都設到Bindings里面去,那么我們JS豈不是有很大的自由度了嗎?因為JS里面也可以操作我們的Java對象了,並且我們可以像ava編程一樣的對JS編程了,還不用再編譯,馬上就可以運行.靈活性豈不是變得更高了嗎?


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM