Java學習筆記--腳本語言支持API


Java語言的動態性之腳本語言支持API

       隨着Java平台的流行,很多的腳本語言(scripting language)都可以運行在Java虛擬機啊上,其中比較流行的有JavaScript、JRuby、Jython和Groovy等。相對Java語言來說,腳本語言由於其靈活性非常強,非常適合在某些情況下使用,比如描述應用中復雜多變的業務邏輯,並在應用運行過程中進行動態修改;為應用提供一種領域特定語言(Domainspecific Language,DSL),供沒有技術背景的普通用戶使用;作為應用中各個組件之間的“膠水”,快速進行組件之間的整合;快速開發出應用的原型系統,從而迅速獲取用戶反饋,並進行改進;幫助開發人員快速編寫測試用例等。等於這些場景,如果使用java來開發則事倍功半。

       對於這些運行在Java虛擬機平台上的腳本語言來說,並不需要為她們准備額外的運行環境,直接復用已有的Java虛擬機環境即可。這就節省了在運行環境上所需的成本投入。在應用開發中使用腳本語言,實際上是“多語言開發”的一種很好的實踐,即根據應用的需要和語言本身的特性來選擇最合適的變成語言,以快速高效地解決應用中的某一部分問題。多種不同語言實現的組件組合起來,用Java編寫核心業務邏輯,用Ruby來進行數據處理。不同語言編寫的代碼可以同時運行的同一個Java虛擬機之上。這些腳本語言和Java語言之間的交互,是由腳本語言支持API來完成的。

1.腳本引擎

       一段腳本的執行需要由該腳本語言對應的腳本引擎來完成。一個Java程序可以選擇同時包含多種腳本語言的執行引擎,這完全由程序的需求來決定。程序中所用到的腳本語言,都需要有相應的腳本引擎。JSR 233中定義了腳本引擎的注冊和查找機制。這對於腳本引擎的實現者來說,是需要了解的。而一般的開發人員只需要了解如何通過腳本引擎管理器來獲取對應語言的腳本引擎,並不需要了解腳本引擎的注冊機制。Java SE6中自帶了JavaScript語言的腳本引擎,是基於Mozilla的Rhino來實現的。對於其他的腳本語言,則需要下載對應的腳本引擎的庫並放在程序的類路徑中。一般只要放在類路徑中中,腳本引擎就可以被應用程序發現並使用。
        首先介紹腳本引擎的一般用法。首先創建一個腳本引擎管理器javax.script.ScriptEngineManager對象,再通過管理器來查詢所需的JavaScript腳本引擎,最后通過腳本引擎來執行JavaScript代碼。

ScriptEngineManagerDemo

ScriptEngineManagerDemo

執行結果

執行結果

       上面的代碼中是通過腳本引擎的名字進行查找的。實際上,腳本引擎管理共支持三種查找腳本引擎的方式,分別通過名稱、文件擴展名和MIME類型來完成。

2.語言綁定

       腳本語言支持API的一個很大優勢在於它規范了Java語言與腳本語言之間的交互方式,使Java語言編寫的程序可以與腳本之間進行雙向的方法調用和數據傳遞。方法調用的方式會在稍后介紹。數據傳遞是通過語言綁定對象來完成的。所謂的綁定對象就是一個簡單的哈希表,用來存放和獲取需要共享的數據。所有數據都對應這個哈希表中的一個條目,是簡單的名值對。接口javax.script.Bindings定義了語言綁定對象的接口,繼承自java.util.Map接口。一個腳本引擎在執行過程中可能會使用多個語言綁定對象。不同語言綁定對象的作用域不同。在默認情況下,腳本引擎會提供多個語言綁定對象,用來存放在執行過程中產生全局對象等。ScriptEngine類提供了put和get方法對腳本引擎中特定作用域的默認語言綁定對象進行操作。程序可以直接使用這個默認的語言綁定對象,也可以使用自己的語言綁定對象。在腳本語言的執行過程中,可以將語言綁定對象看成是一個額外的變量映射表。在解析變量值的時候,語言綁定對象中的名稱也會被考慮在內。腳本執行過程中產生的全局變量等內容,會出現在語言綁定對象中。通過這種方式就完成了Java與腳本語言之間的雙向數據傳遞。

腳本引擎默認的語言綁定對象的示例

    /**
    * 腳本引擎默認的語言綁定對象的示例
     */
    public static void useDefaultBinding(){
        try{
            ScriptEngine  engine = getJavaScriptEngine();
            engine.put("name","Arthur");
            engine.eval("var message = 'Hello,' + name;");
            engine.eval("print(message);");
            Object obj = engine.get("message");
            System.out.println(obj);
        }catch (Exception e){
            System.out.println("異常信息:"+e.getMessage());
        }
    }

腳本引擎默認的語言綁定對象的示例執行結果

腳本引擎默認的語言綁定對象的示例執行結果

    在大多數情況下,使用ScriptEngine的put和get方法就足夠了。如果僅使用put和get方法,語言綁定對象本身對於開發人員來說是透明的。在某些情況下,需要使用程序自己的語言綁定對象,比如語言綁定對象中包含了程序自己獨有的數據。如果希望使用自己的語言綁定對象,可以調用腳本引擎的createBindings方法或創建,並傳遞給腳本引擎的eval方法。

自定義語言綁定對象的示例

    /**
     * 自定義語言綁定對象的示例
     */
    public static void userCustomBinding(){
        try{
            ScriptEngine engine = getJavaScriptEngine();
            Bindings bindings = new SimpleBindings();
            bindings.put("hobby","playe games");
            engine.eval("print('I like ' + hobby);",bindings);
        }catch(Exception e){
            System.out.println("異常信息:"+e.getMessage());
        }
    }

自定義語言綁定對象的示例的執行結果

自定義語言綁定對象的示例的執行結果

    通過eval方法傳遞的語綁定對象,僅在當前eval調用中生效,並不會改變引擎默認的語言綁定對象。

3.腳本執行上下文

    與腳本引擎執行相關的另外一個重要的接口是javax.script.ScriptContext,其中包含腳本引擎執行過程的相關上下文信息,可以通過與Java EE中servlet規范中的javax.servlet.ServletContext接口來進行類比。腳本引擎通過此上下文對象獲取與腳本執行相關的信息,也允許開發人員通過此對象來配置腳本引擎的行為。該上下文對象主要包含以下3類信息:

3.1輸入與輸出

    首先介紹與腳本輸入和輸出的配置信息,其中包括腳本在執行中用來讀取數據輸入的java.io.Reader對象以及輸出正確內容和出錯信息的java.io.Writer對象。在默認情況下,腳本的輸入輸出都在發生在標准控制台中。

把腳本運行時的輸出寫入到文件中的示例

    /**
     * 把腳本運行時的輸出寫入到文件中。
     */
    public static void scriptToFile(){
        try {
            ScriptEngine engine = getJavaScriptEngine();
            ScriptContext context = engine.getContext();
            context.setWriter(new FileWriter("output.txt"));
            engine.eval("print('Hello World!');");
        } catch (ScriptException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

執行結果

把腳本運行時的輸出寫入到文件中

說明
    通過setWriter方法把腳本的輸出重定向到一個文件中。通過ScriptContext的setReader和setErrorWriter方法可以分別設置腳本執行時的數據輸入來源和產生錯誤時出錯信息的輸出目的。

3.2自定義屬性

    ScriptContext中也有與ServletConext中類似的獲取和設置屬性的方法,即setAttribute和getAttribute。所不同的是,ScriptContext中的屬性是有作用域之分的。不同作用域的區別在於查找屬性時的順序不同。每個作用域都以一個對應的整數表示其查找順序。該整數值越小,說明查找時的順序越優先。優先級高的作用域中的屬性會隱藏優先級低的作用域中的同名屬性。因此,設置屬性時需要顯式地指定所在地作用域。在獲取屬性地時候,既可以選擇在指定地作用域中查找,也可以選擇根據作用域優先級自動進行查找。
    不過腳本執行上下文實現中包含地作用域是固定的,開發人員不能隨意定義自己的作用域。通過ScriptContext的getScopes方法可以得到所有可用的作用域列表。ScriptContext中預先定義了兩個作用域:

  • 常量ScriptContext.ENGINE_SCOPE表示的作用域對應的是當前的腳本引擎。
  • 常量ScriptContext.GLOBAL_SCOPE表示的作用域對應的是從同一引擎工廠中創建出來的所有腳本引擎對象。

    說明: 前者的優先級較高

作用域影響同名屬性查找示例

   /**
     * 作用域影響同名屬性查找的示例
     */
    public static void scriptContextAttribute(){
        try{
            ScriptEngine engine = getJavaScriptEngine();
            ScriptContext context = engine.getContext();
            context.setAttribute("name","Arthur Ming",ScriptContext.GLOBAL_SCOPE);
            context.setAttribute("name","明國賓",ScriptContext.ENGINE_SCOPE);
            System.out.println(context.getAttribute("name"));
            System.out.println(context.getAttribute("name",ScriptContext.GLOBAL_SCOPE));
        } catch(Exception e){
            System.out.println("異常信息:"+e.getMessage());
        }
    }

執行結果

作用域影響同名屬性查找示例

3.1語言綁定對象

    腳本執行上下文中的最后一類信息是語言綁定對象。語言綁定對象也是與作用域相對應的,同樣的作用域優先級順序對語言綁定對象也適用。這樣的優先級順序會對腳本執行時的變量解析產生影響。

語言綁定對象的優先級順序的示例

    /**
     * 語言綁定對象的優先級順序的示例
     */
    public static void scriptContextBindings(){
        try {
            ScriptEngine engine = getJavaScriptEngine();
            ScriptContext context = engine.getContext();
            Bindings binding1 = engine.createBindings();
            binding1.put("name","Arthur Ming");
            context.setBindings(binding1,ScriptContext.GLOBAL_SCOPE);
            Bindings binding2 = engine.createBindings();
            binding2.put("name","明國賓");
            context.setBindings(binding2,ScriptContext.ENGINE_SCOPE);
            engine.eval("print(name)");
        } catch (Exception e){
            e.printStackTrace();
        }
    }

執行結果

語言綁定對象的優先級順序的示例

    通過ScriptContext的setBindings方法設置的語言綁定對象會影響到ScriptEngine在執行腳本時變量解析。ScriptEngine的put和get方法所操作的實際上就是ScriptContext中的作用域為ENGINE_SCOPE的語言綁定對象。

通過腳本執行上下文獲取語言綁定對象的示例

   /**
     * 通過腳本執行上下文獲取語言綁定對象的示例
     */
    public static void useScriptContextValues(){
        try{
            ScriptEngine engine = getJavaScriptEngine();
            ScriptContext context = engine.getContext();
            Bindings bindings = context.getBindings(ScriptContext.ENGINE_SCOPE);
            bindings.put("name","Arthur Ming");
            engine.eval("print(name)");
        } catch (Exception e){
            e.printStackTrace();
        }
    }

執行結果

通過腳本執行上下文獲取語言綁定對象的示例

    自定義屬性實際上也保存在語言綁定對象中。

自定義屬性保存在語言綁定對象中示例

    /**
     * 自定義屬性保存在語言綁定對象中示例
     */
    public static void attributeInBindings(){
        try{
            ScriptEngine engine = getJavaScriptEngine();
            ScriptContext context = engine.getContext();
            context.setAttribute("name1","Arthur Ming",ScriptContext.GLOBAL_SCOPE);
            context.setAttribute("name2","明國賓",ScriptContext.ENGINE_SCOPE);
            engine.eval("print(name1);");
            engine.eval("print(name2);");
        }catch (Exception e){
            e.printStackTrace();
        }
    }

執行結果

自定義屬性保存在語言綁定對象中示例


免責聲明!

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



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