我們不需要將動態(tài)語言編譯為 Java字節(jié)碼就可以在 Java 應(yīng)用程序中使用它們。使用 Java Platform, Standard Edition 6 (Java SE)中添加的腳本包(并且向后兼容 Java SE 5),Java 代碼可以在運行時以一種簡單的、統(tǒng)一的方式調(diào)用多種動態(tài)語言。本系列文章共分兩個部分,第 1 部分將介紹 Java 腳本 API 的各種特性。文章將使用一個簡單的 Hello World 應(yīng)用程序展示 Java 代碼如何執(zhí)行腳本代碼以及腳本如何反過來執(zhí)行 Java 代碼。第 2 部分將深入研究 Java 腳本 API 的強大功能。
Java 開發(fā)人員清楚 Java 并不是在任何情況下都是最佳的語言。今年,1.0 版本的 JRuby 和 Groovy 的發(fā)行引領(lǐng)了一場熱潮,促使人們紛紛在自己的 Java 應(yīng)用程序中添加動態(tài)語言。Groovy、JRuby、Rhino、Jython 和一些其他的開源項目使在所謂的腳本語言中編寫代碼并在 JVM 中運行成為了可能(請參閱 參考資料)。通常,在 Java 代碼中集成這些語言需要對各種解釋器所特有的 API 和特性有所了解。
Java SE 6 中添加的 javax.script 包使集成動態(tài)語言更加容易。通過使用一小組接口和具體類,這個包使我們能夠簡單地調(diào)用多種腳本語言。但是,Java 腳本 API 的功能不只是在應(yīng)用程序中編寫腳本;這個腳本包使我們能夠在運行時讀取和調(diào)用外部腳本,這意味著我們可以動態(tài)地修改這些腳本從而更改運行應(yīng)用程序的行為。
Java 腳本 API
腳本與動態(tài)的對比
術(shù)語腳本 通常表示在解釋器 shell 中運行的語言,它們往往沒有單獨的編譯步驟。術(shù)語動態(tài) 通常表示等到運行時判斷變量類型或?qū)ο笮袨榈恼Z言,往往具有閉包和連續(xù)特性。一些通用的編程語言同時具有這兩種特性。此處首選腳本語言 是因為本文的著重點是 Java 腳本 API,而不是因為提及的語言缺少動態(tài)特性。
2006 年 10 月,Java 語言添加了腳本包,從而提供了一種統(tǒng)一的方式將腳本語言集成到 Java 應(yīng)用程序中去。對于語言開發(fā)人員,他們可以使用這個包編寫粘連代碼(glue code),從而使人們能夠在 Java 應(yīng)用程序中調(diào)用他們的語言。對于 Java 開發(fā)人員,腳本包提供了一組類和接口,允許使用一個公共 API 調(diào)用多種語言編寫的腳本。因此,腳本包類似于不同語言(比如說不同的數(shù)據(jù)庫)中的 Java Database Connectivity (JDBC) 包,可以使用一致的接口集成到 Java 平臺中去。
以前,在 Java 代碼中,動態(tài)調(diào)用腳本語言涉及到使用各種語言發(fā)行版所提供的獨特類或使用 Apache 的 Jakarta Bean Scripting Framework (BSF)。BSF 在一個 API 內(nèi)部統(tǒng)一了一組腳本語言(請參閱 參考資料)。使用 Java SE 6 腳本 API,二十余種腳本語言(AppleScript、Groovy、JavaScript、Jelly、PHP、Python、Ruby 和 Velocity)都可以集成到 Java 代碼中,這在很大程序上依賴的是 BSF。
腳本 API 在 Java 應(yīng)用程序和外部腳本之間提供了雙向可見性。Java 代碼不僅可以調(diào)用外部腳本,而且還允許那些腳本訪問選定的 Java 對象。比如說,外部 Ruby 腳本可以對 Java 對象調(diào)用方法,并訪問對象的屬性,從而使腳本能夠?qū)⑿袨樘砑拥竭\行中的應(yīng)用程序中(如果在開發(fā)時無法預(yù)計應(yīng)用程序的行為)。
調(diào)用外部腳本可用于運行時應(yīng)用程序增強、配置、監(jiān)控或一些其他的運行時操作,比如說在不停止應(yīng)用程序的情況下修改業(yè)務(wù)規(guī)則。腳本包可能的作用包括:
·在比 Java 語言更簡單的語言中編寫業(yè)務(wù)規(guī)則,而不用借助成熟的規(guī)則引擎。
·創(chuàng)建插件架構(gòu),使用戶能夠動態(tài)地定制應(yīng)用程序。
·將已有腳本集成到 Java 應(yīng)用程序中,比如說處理或轉(zhuǎn)換文件文章的腳本。
·使用成熟的編程語言(而不是屬性文件)從外部配置應(yīng)用程序的運行時行為。
·在 Java 應(yīng)用程序中添加一門特定于域的語言(domain-specific language)。
·在開發(fā) Java 應(yīng)用程序原型的過程中使用腳本語言。
·在腳本語言中編寫應(yīng)用程序測試代碼。
你好,腳本世界
HelloScriptingWorld 類(本文中的相關(guān)代碼均可從 下載部分 獲得)演示了 Java 腳本包的一些關(guān)鍵特性。它使用硬編碼的 JavaScript 作為示例腳本語言。此類的 main() 方法(如清單 1 所示)將創(chuàng)建一個 JavaScript 腳本引擎,然后分別調(diào)用五個方法(在下文的清單中有顯示)用于突出顯示腳本包的特性。
清單 1. HelloScriptingWorld main 方法
| public static void main(String[] args) throws ScriptException, NoSuchMethodException { ScriptEngineManager scriptEngineMgr = new ScriptEngineManager(); ScriptEngine jsEngine = scriptEngineMgr.getEngineByName("JavaScript"); if (jsEngine == null) { System.err.println("No script engine found for JavaScript"); System.exit(1); } System.out.println("Calling invokeHelloScript..."); invokeHelloScript(jsEngine); System.out.println("nCalling defineScriptFunction..."); defineScriptFunction(jsEngine); System.out.println("nCalling invokeScriptFunctionFromEngine..."); invokeScriptFunctionFromEngine(jsEngine); System.out.println("nCalling invokeScriptFunctionFromJava..."); invokeScriptFunctionFromJava(jsEngine); System.out.println("nCalling invokeJavaFromScriptFunction..."); invokeJavaFromScriptFunction(jsEngine); } |
main() 方法的主要功能是獲取一個 javax.script.ScriptEngine 實例(清單 1 中的前兩行代碼)。腳本引擎可以在特定的語言中加載并執(zhí)行腳本。它是 Java 腳本包中使用最為頻繁、作用最為重要的類。我們從 javax.script.ScriptEngineManager 獲取一個腳本引擎(第一行代碼)。通常,程序只需要獲取一個腳本引擎實例,除非使用了很多種腳本語言。
ScriptEngineManager 類
ScriptEngineManager 可能是腳本包中惟一一個經(jīng)常使用的具體類;其他大多數(shù)都是接口。它或許是腳本包中惟一的一個要直接或間接地(通過 Spring Framework 之類的依賴性注入機制)實例化的類。ScriptEngineManager 可以使用以下三種方式返回腳本引擎:
·通過引擎或語言的名稱,比如說 清單 1 請求 JavaScript 引擎。
·通過該語言腳本共同使用的文件擴展名,比如說 Ruby 腳本的 .rb。
·通過腳本引擎聲明的、知道如何處理的 MIME 類型。
本文示例為什么要使用 JavaScript?
本文中的 Hello World 示例使用了部分 JavaScript 腳本,這是因為 JavaScript 代碼易于理解,不過主要還是因為 Sun Microsystems 和 BEA Systems 所提供的 Java 6 運行時環(huán)境附帶有基于 Mozilla Rhino 開源 JavaScript 實現(xiàn)的 JavaScript 解釋器。使用 JavaScript,我們無需在類路徑中添加腳本語言 JAR 文件。
ScriptEngineManager 間接查找和創(chuàng)建腳本引擎。也就是說,當(dāng)實例化腳本引擎管理程序時,ScriptEngineManager 會使用 Java 6 中新增的服務(wù)發(fā)現(xiàn)機制在類路徑中查找所有注冊的 javax.script.ScriptEngineFactory 實現(xiàn)。這些工廠類封裝在 Java 腳本 API 實現(xiàn)中;也許您永遠都不需要直接處理這些工廠類。
ScriptEngineManager 找到所有的腳本引擎工廠類之后,它會查詢各個類并判斷是否能夠創(chuàng)建所請求類型的腳本引擎 —— 清單 1 中為 JavaScript 引擎。如果工廠說可以創(chuàng)建所需語言的腳本引擎,那么管理程序?qū)⒁蠊S創(chuàng)建一個引擎并將其返回給調(diào)用者。如果沒有找到所請求語言的工廠,那么管理程序?qū)⒎祷?null,清單 1 中的代碼將檢查 null 返回值并做出預(yù)防。
ScriptEngine 接口
如前所述,代碼將使用 ScriptEngine 實例執(zhí)行腳本。腳本引擎充當(dāng)腳本代碼和最后執(zhí)行代碼的底層語言解釋器或編譯器之間的中間程序。這樣,我們就不需要了解各個解釋器使用哪些類來執(zhí)行腳本。比如說,JRuby 腳本引擎可以將代碼傳遞給 JRuby 的 org.jruby.Ruby 類的一個實例,首先將腳本編譯成中間形式,然后再調(diào)用它計算腳本并處理返回值。腳本引擎實現(xiàn)隱藏了一些細節(jié),包括解釋器如何與 Java 代碼共享類定義、應(yīng)用程序?qū)ο蠛洼斎?輸出流。
圖 1 顯示了應(yīng)用程序、Java 腳本 API 和 ScriptEngine 實現(xiàn)、腳本語言解釋器之間的總體關(guān)系。我們可以看到,應(yīng)用程序只依賴于腳本 API,它提供了 ScriptEngineManager 類和 ScriptEngine 接口。ScriptEngine 實現(xiàn)組件處理使用特定腳本語言解釋器的細節(jié)。
圖 1:腳本 API 組件關(guān)系
您可能會問:如何才能獲取腳本引擎實現(xiàn)和語言解釋器所需的 JAR 文件呢?最好的方法是在 java.net 上托管的開源 Scripting 項目中查找腳本引擎實現(xiàn)(請參閱 參考資料)。您可以在 java.net 上找到許多語言的腳本引擎實現(xiàn)和其他網(wǎng)站的鏈接。Scripting 項目還提供了各種鏈接,通過這些鏈接可以下載受支持的腳本語言的解釋器。
在 清單 1 中,main() 方法將 ScriptEngine 傳遞給各個方法用于計算該方法的 JavaScript 代碼。第一個方法如清單 2 所示。invokeHelloScript() 方法調(diào)用腳本引擎的 eval 方法計算和執(zhí)行 JavaScript 代碼中的特定字符串。ScriptEngine 接口定義了 6 個重載的 eval() 方法,用于將接收的腳本當(dāng)作字符串或 java.io.Reader 對象計算,java.io.Reader 對象一般用于從外部源(例如文件)讀取腳本。
清單 2. invokeHelloScript 方法
| private static void invokeHelloScript(ScriptEngine jsEngine) throws ScriptException { jsEngine.eval("println('Hello from JavaScript')"); } |
HelloScriptingWorld 應(yīng)用程序中的示例腳本 使用 JavaScript println() 函數(shù)向控制臺輸出結(jié)果,但是我們擁有輸入和輸出流的完全控制權(quán)。腳本引擎提供了一個選項用于修改腳本執(zhí)行的上下文,這意味著我們可以修改標準輸入流、標準輸出流和標準錯誤流,同時還可以定義哪些全局變量和 Java 對象對正在執(zhí)行的腳本可用。
invokeHelloScript() 方法中的 JavaScript 將 Hello from JavaScript 輸出到標準輸出流,在本例中為控制臺窗口。(清單 6 含有運行 HelloScriptingWorldApplication 時的完整輸出。)
注意,類中的這一方法和其他方法都聲明拋出了 javax.script.ScriptException。這個選中的異常(腳本包中定義的惟一一個異常)表示引擎無法解析或執(zhí)行給定的代碼。所有腳本引擎 eval() 方法都聲明拋出一個 ScriptException,因此我們的代碼需要適當(dāng)處理這些異常。
清單 3 顯示了兩個有關(guān)的方法:defineScriptFunction() 和 invokeScriptFunctionFromEngine()。defineScriptFunction() 方法還使用一段硬編碼的 JavaScript 代碼調(diào)用腳本引擎的 eval() 方法。但是有一點需要注意,該方法的所有工作只是定義了一個 JavaScript 函數(shù) sayHello()。并沒有執(zhí)行任何代碼。sayHello() 函數(shù)只有一個參數(shù),它會使用 println() 語句將這個參數(shù)輸出到控制臺。腳本引擎的 JavaScript 解釋器將這個函數(shù)添加到全局環(huán)境,以供后續(xù)的 eval 調(diào)用使用(該調(diào)用發(fā)生在 invokeScriptFunctionFromEngine() 方法中,這并不奇怪)。
清單 3. defineScriptFunction 和 invokeScriptFunctionFromEngine 方法
| private static void defineScriptFunction(ScriptEngine engine) throws ScriptException { // Define a function in the script engine engine.eval( "function sayHello(name) {" + " println('Hello, ' + name)" + "}" ); } private static void invokeScriptFunctionFromEngine(ScriptEngine engine) throws ScriptException { engine.eval("sayHello('World!')"); } |
這兩個方法演示了腳本引擎可以維持應(yīng)用程序組件的狀態(tài),并且能夠在后續(xù)的 eval() 方法調(diào)用過程中使用其狀態(tài)。invokeScriptFunctionFromEngine() 方法可以利用所維持的狀態(tài),方法是調(diào)用定義在 eval() 調(diào)用中的 sayHello() JavaScript 函數(shù)。
許多腳本引擎在 eval() 調(diào)用之間維持全局變量和函數(shù)的狀態(tài)。但是有一點值得格外注意,Java 腳本 API 并不要求腳本引擎提供這一特性。本文中所使用的 JavaScript、Groovy 和 JRuby 腳本引擎確實在 eval() 調(diào)用之間維持了這些狀態(tài)。
清單 4 中的代碼在前一個示例的基礎(chǔ)上做了幾分修改。原來的 invokeScriptFunctionFromJava() 方法在調(diào)用 sayHello() JavaScript 函數(shù)時沒有使用 ScriptEngine 的 eval() 方法或 JavaScript 代碼。與此不同,清單 4 中的方法使用 Java 腳本 API 的 javax.script.Invocable 接口調(diào)用由腳本引擎所維持的函數(shù)。invokeScriptFunctionFromJava() 方法將腳本引擎對象傳遞給 Invocable 接口,然后對該接口調(diào)用 invokeFunction() 方法,最終使用給定的參數(shù)調(diào)用 sayHello() JavaScript 函數(shù)。如果調(diào)用的函數(shù)需要返回值,則 invokeFunction() 方法會將值封裝為 Java 對象類型并返回。
清單 4. invokeScriptFunctionFromJava 方法
| private static void invokeScriptFunctionFromJava(ScriptEngine engine) throws ScriptException, NoSuchMethodException { Invocable invocableEngine = (Invocable) engine; invocableEngine.invokeFunction("sayHello", "from Java"); } |
使用代理實現(xiàn)高級腳本調(diào)用
當(dāng)腳本函數(shù)或方法實現(xiàn)了一個 Java 接口時,就可以使用高級 Invocable。Invocable 接口定義了一個 getInterface() 方法,該方法使用接口做為參數(shù)并且將返回一個實現(xiàn)該接口的 Java 代碼對象。從腳本引擎獲得代理對象之后,可以將它作為正常的 Java 對象對待。對該代理調(diào)用的方法將委托給腳本引擎通過腳本語言執(zhí)行。
注意,清單 4 中沒有 JavaScript 代碼。Invocable 接口允許 Java 代碼調(diào)用腳本函數(shù),而無需知道其實現(xiàn)語言。如果腳本引擎無法找到給定名稱或參數(shù)類型的函數(shù),那么 invokeFunction() 方法將拋出一個 java.lang.NoSuchMethodException。
Java 腳本 API 并不要求腳本引擎實現(xiàn) Invocable 接口。實際上,清單 4 中的代碼應(yīng)該使用 instanceof 運算符確保腳本引擎在轉(zhuǎn)換(cast)之前實現(xiàn)了 Invocable 接口。
通過腳本代碼調(diào)用 Java 方法
清單 3 和 清單 4 中的示例展示了 Java 代碼如何調(diào)用腳本語言中定義的函數(shù)或方法。您可能會問:腳本語言中編寫的代碼是否可以反過來對 Java 對象調(diào)用方法呢?答案是可以。清單 5 中的 invokeJavaFromScriptFunction() 方法顯示了如何使腳本引擎能夠訪問 Java 對象,以及腳本代碼如何才能對這些 Java 對象調(diào)用方法。明確的說,invokeJavaFromScriptFunction() 方法使用腳本引擎的 put() 方法將 HelloScriptingWorld 類的實例本身提供給引擎。當(dāng)引擎擁有 Java 對象的訪問權(quán)之后(使用 put() 調(diào)用所提供的名稱),eval() 方法腳本中的腳本代碼將使用該對象。
清單 5. invokeJavaFromScriptFunction 和 getHelloReply 方法
| private static void invokeJavaFromScriptFunction(ScriptEngine engine) throws ScriptException { engine.put("helloScriptingWorld", new HelloScriptingWorld()); engine.eval( "println('Invoking getHelloReply method from JavaScript...'); " + "var msg = helloScriptingWorld.getHelloReply(vJavaScript'); " + "println('Java returned: ' + msg)" ); } /** Method invoked from the above script to return a string. */ public String getHelloReply(String name) { return "Java method getHelloReply says, 'Hello, " + name + "'"; } |
清單 5 中的 eval() 方法調(diào)用中所包含的 JavaScript 代碼使用腳本引擎的 put() 方法調(diào)用所提供的變量名稱 helloScriptingWorld 訪問并使用 HelloScriptingWorld Java 對象。清單 5 中的第二行 JavaScript 代碼將調(diào)用 getHelloReply() 公有 Java 方法。getHelloReply() 方法將返回 Java method getHelloReply says, 'Hello, ' 字符串。eval() 方法中的 JavaScript 代碼將 Java 返回值賦給 msg 變量,然后再將其打印輸出給控制臺。
Java 對象轉(zhuǎn)換
當(dāng)腳本引擎使運行于引擎環(huán)境中的腳本能夠使用 Java 對象時,引擎需要將其封裝到適用于該腳本語言的對象類型中。封裝可能會涉及到一些適當(dāng)?shù)膶ο?值轉(zhuǎn)換,比如說允許 Java Integer 對象直接在腳本語言的數(shù)學(xué)表達式中使用。關(guān)于如何將 Java 對象轉(zhuǎn)換為腳本對象的研究是與各個腳本語言的引擎特別相關(guān)的,并且不在本文的討論范圍之內(nèi)。但是,您應(yīng)該意識到轉(zhuǎn)換的發(fā)生,因為可以通過測試來確保所使用的腳本語言執(zhí)行轉(zhuǎn)換的方式符合您的期望。
ScriptEngine.put 及其相關(guān) get() 方法是在運行于腳本引擎中的 Java 代碼和腳本之間共享對象和數(shù)據(jù)的主要途徑。(有關(guān)這一方面的詳細論述,請參閱本文后面的 Script-execution scope 一節(jié)。)當(dāng)我們調(diào)用引擎的 put() 方法時,腳本引擎會將第二個參數(shù)(任何 Java 對象)關(guān)聯(lián)到特定的字符串關(guān)鍵字。大多數(shù)腳本引擎都是讓腳本使用特定的變量名稱來訪問 Java 對象。腳本引擎可以隨意對待傳遞給 put() 方法的名稱。比如說,JRuby 腳本引擎讓 Ruby 代碼使用全局 $helloScriptingWorld 對象訪問 helloScriptingWorld,以符合 Ruby 全局變量的語法。
腳本引擎的 get() 方法檢索腳本環(huán)境中可用的值。一般而言,Java 代碼通過 get() 方法可以訪問腳本環(huán)境中的所有全局變量和函數(shù)。但是只有明確使用 put() 與腳本共享的 Java 對象才可以被腳本訪問。
外部腳本在運行著的應(yīng)用程序中訪問和操作 Java 對象的這種功能是擴展 Java 程序功能的一項強有力的技巧。(第 2 部分將通過示例研究這一技巧)。
運行 HelloScriptingWorld 應(yīng)用程序
您可以通過下載和構(gòu)建源代碼來運行 HelloScriptingWorld 應(yīng)用程序。此 .zip 中文件含有一個 Ant 腳本和一個 Maven 構(gòu)建腳本,可以幫助大家編譯和運行示例應(yīng)用程序。請執(zhí)行以下步驟:
·下載 此 .zip 文件。
·創(chuàng)建一個新目錄,比如說 java-scripting,并將步驟 1 中所下載的文件解壓到該目錄中。
·打開命令行 shell 并轉(zhuǎn)到該目錄。
·運行 ant run-hello 命令。
您應(yīng)該可以看到類似于清單 6 的 Ant 控制臺輸出。注意,defineScriptFunction() 函數(shù)沒有產(chǎn)生任何輸出,因為它雖然定義了輸出但是卻沒有調(diào)用 JavaScript 函數(shù)。
清單 6. 運行 HelloScriptingWorld 時的輸出
| Calling invokeHelloScript... Hello from JavaScript Calling defineScriptFunction... Calling invokeScriptFunctionFromEngine... Hello, World! Calling invokeScriptFunctionFromJava... Hello, from Java Calling invokeJavaFromScriptFunction... Invoking getHelloReply method from JavaScript... Java returned: Java method getHelloReply says, 'Hello, JavaScript' |
Java 5 兼容性
Java SE 6 引入了 Java 腳本 API,但是您也可以使用 Java SE 5 運行此 API。只需要提供缺少的 javax.script 包類的一個實現(xiàn)即可。所幸的是,Java Specification Request 223 參考實現(xiàn)中含有這個實現(xiàn)(請參閱 參考資料 獲得下載鏈接。)JSR 223 對 Java 腳本 API 做出了定義。
如果您已經(jīng)下載了 JSR 223 參考實現(xiàn),解壓下載文件并將 script-api.jar、script-js.jar 和 js.jar 文件復(fù)制到您的類路徑下。這些文件將提供腳本 API、JavaScript 腳本引擎接口和 Java SE 6 中所附帶的 JavaScript 腳本引擎。
腳本執(zhí)行作用域
與簡單地調(diào)用引擎的 get() 和 put() 方法相比,如何將 Java 對象公開給運行于腳本引擎中的腳本具有更好的可配置性。當(dāng)我們在腳本引擎上調(diào)用 get() 或 put() 方法時,引擎將會在 javax.script.Bindings 接口的默認實例中檢索或保存所請求的關(guān)鍵字。(Bindings 接口只是一個 Map 接口,用于強制關(guān)鍵字為字符串。)
當(dāng)代碼調(diào)用腳本引擎的 eval() 方法時,將使用引擎默認綁定的關(guān)鍵字和值。但是,您可以為 eval() 調(diào)用提供自己的 Bindings 對象,以限制哪些變量和對象對于該特定腳本可見。該調(diào)用外表上類似于 eval(String, Bindings) 或 eval(Reader, Bindings)。要幫助您創(chuàng)建自定義的 Bindings,腳本引擎將提供一個 createBindings() 方法,該方法和返回值是一個內(nèi)容為空的 Bindings 對象。使用 Bindings 對象臨時調(diào)用 eval 將隱藏先前保存在引擎默認綁定中的 Java 對象。
要添加功能,腳本引擎含有兩個默認綁定:其一為 get() 和 put() 調(diào)用所使用的 “引擎作用域” 綁定 ;其二為 “全局作用域” 綁定,當(dāng)無法在 “引擎作用域” 中找到對象時,引擎將使用第二種綁定進行查找。腳本引擎并不需要使腳本能夠訪問全局綁定。大多數(shù)腳本都可以訪問它。
“全局作用域” 綁定的設(shè)計目的是在不同的腳本引擎之間共享對象。ScriptEngineManager 實例返回的所有腳本引擎都是 “全局作用域” 綁定對象。您可以使用 getBindings(ScriptContext.GLOBAL_SCOPE) 方法檢索某個引擎的全局綁定,并且可以使用 setBindings(Bindings, ScriptContext.GLOBAL_SCOPE) 方法為引擎設(shè)置全局綁定。
ScriptContext 是一個定義和控制腳本引擎運行時上下文的接口。腳本引擎的 ScriptContext 含有 “引擎” 和 “全局” 作用域綁定,以及用于標準輸入和輸出操作的輸入和輸出流。您可以使用引擎的 getContext() 方法獲取并操作腳本引擎的上下文。
一些腳本 API 概念,比如說作用域、綁定 和上下文,開始看來會令人迷惑,因為它們的含義有交叉的地方。本文的源代碼下載文件含有一個名為 ScriptApiRhinoTest 的 JUnit 測試文件,位于 src/test/java directory 目錄,該文件可以通過 Java 代碼幫助解釋這些概念。
未來的計劃
現(xiàn)在,大家已經(jīng)對 Java 腳本 API 有了最基本的認識,本系列文章的第 2 部分將在此基礎(chǔ)上進行擴展,為大家演示一個更為實際的示例應(yīng)用程序。該應(yīng)用程序?qū)⑹褂?Groovy、Ruby 和 JavaScript 一起編寫的外部腳本文件來定義可在運行時修改的業(yè)務(wù)邏輯。如您如見,在腳本語言中定義業(yè)務(wù)規(guī)則可以使規(guī)則的編寫更加輕松,并且更易于程序員之外的人員閱讀,比如說業(yè)務(wù)分析師或規(guī)則編寫人員。
新聞熱點
疑難解答
圖片精選