腳本語言的一些有用的特性是:
方便:大多數腳本語言都是動態類型的。您通常可以建立新的變量,而不聲明變數類型,並且可以重複使用變數來儲存不同類型的物件。此外,腳本語言往往會自動執行許多類型的轉換,例如, 必要時 將數字10轉換為「10」。
開發快速原型:您可以避免編輯編譯運行週期,只使用「編輯運行」!
應用程式/自訂:你可以「具體化」的部分應用程序,例如一些配置腳本,業務邏輯/規則和財務應用中的數學表達式 。
為應用程式新增命令列模式,用於偵錯、執行時間配置/部署時間。現在大多數應用程式都有一個基於Web的GUI設定工具。但是系統管理員/部署人員常常喜歡命令列工具。一個「標準」的腳本語言可以用來實現這個目的,而不是發明特設的腳本語言。
Java 腳本 API 是一種獨立於框架的腳本語言,使用來自於Java程式碼的腳本引擎 。透過java腳本API,可以使用Java語言編寫自訂/可擴展的應用程式並將自訂腳本語言選擇留給最終用戶 。 Java 應用程式開發者不需要在開發過程中選擇擴充語言。如果你使用JSR-223 API來編寫應用,那麼你的使用者可以使用任何JSR-223相容的腳本語言。
Java 腳本功能是在 javax.script 套件中。這是一個比較小的,簡單的API。腳本的出發點是 ScriptEngineManager 類別。一個 ScriptEngineManager 物件可以透過jar檔案的服務發現機制發現腳本引擎。它也可以實例化腳本引擎來解釋使用特定的腳本語言編寫的腳本。使用腳本程式介面的最簡單的方法如下:
建立一個 ScriptEngineManager
物件
從 ScriptEngineManager
取得 ScriptEngine
物件
#使用 ScriptEngine的eval方法執行腳本
#現在,是時候看一些樣本代碼了。了解一些JavaScript有助於閱讀這些例子,但不是強迫的。
從ScriptEngineManager實例中,我們透過 getEngineByName
方法得到一個JavaScript引擎實例。透過腳本引擎的eval方法來執行給定的JavaScript程式碼。為簡便起見,本例以及隨後的例子中,我們不會對異常進行處理。 javax.script API有檢查和運行時異常,你必須妥善處理異常。
import javax.script.*; public class EvalScript { public static void main(String[] args) throws Exception { // create a script engine manager ScriptEngineManager factory = new ScriptEngineManager(); // create a JavaScript engine ScriptEngine engine = factory.getEngineByName("JavaScript"); // evaluate JavaScript code from String engine.eval("print('Hello, World')"); } }
在這個範例中,我們呼叫eval方法來接收java.io.Reader作為輸入來源。讀入的腳本被執行。這種方式能夠成檔執行腳本,用相關的輸入流物件讀取URL和資源。
import javax.script.*; public class EvalFile { public static void main(String[] args) throws Exception { // create a script engine manager ScriptEngineManager factory = new ScriptEngineManager(); // create JavaScript engine ScriptEngine engine = factory.getEngineByName("JavaScript"); // evaluate JavaScript code from given file - specified by first argument engine.eval(new java.io.FileReader(args[0])); } }
假設我們有一個叫」test.js」的文件,裡面的內容如下:
println("This is hello from test.js");
我們可以使用下面的方式來運行剛剛的腳本
java EvalFile test.js
當你的java應用程式嵌入腳本引擎和腳本,你可能希望將您的應用程式物件為全域變數暴露於腳本中。這個例子示範如何將您的應用程式物件作為全域變數暴露於腳本中。我們在應用程式中建立一個 java.io.File物件作為全域變量,名稱是file。該腳本可以存取變量,例如,它可以呼叫它的公共方法。注意存取java物件、領域和方法的語法依賴腳本語言。 JavaScript支援最「自然」的類似java的語法。
public class ScriptVars { public static void main(String[] args) throws Exception { ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName("JavaScript"); File f = new File("test.txt"); // expose File object as variable to script engine.put("file", f); // evaluate a script string. The script accesses "file" // variable and calls method on it engine.eval("print(file.getAbsolutePath())"); } }
有些時候,你可能需要多次呼叫一個特定腳本函數,例如你的應用程式選單功能可能由腳本來實現。在選單中的動作事件處理程式中,可能需要呼叫一個特定的腳本函數。下面的範例示範在Java程式碼呼叫一個特定的腳本。
import javax.script.*; public class InvokeScriptFunction { public static void main(String[] args) throws Exception { ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName("JavaScript"); // JavaScript code in a String String script = "function hello(name) { print('Hello, ' + name); }"; // evaluate script engine.eval(script); // javax.script.Invocable is an optional interface. // Check whether your script engine implements or not! // Note that the JavaScript engine implements Invocable interface. Invocable inv = (Invocable) engine; // invoke the global function named "hello" inv.invokeFunction("hello", "Scripting!!" ); } }
如果你的腳本語言是基於物件(如JavaScript)或物件導向的,你可以在腳本物件上呼叫腳本方法。
import javax.script.*; public class InvokeScriptMethod { public static void main(String[] args) throws Exception { ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName("JavaScript"); // JavaScript code in a String. This code defines a script object 'obj' // with one method called 'hello'. String script = "var obj = new Object(); obj.hello = function(name) { print('Hello, ' + name); }"; // evaluate script engine.eval(script); // javax.script.Invocable is an optional interface. // Check whether your script engine implements or not! // Note that the JavaScript engine implements Invocable interface. Invocable inv = (Invocable) engine; // get script object on which we want to call the method Object obj = engine.get("obj"); // invoke the method named "hello" on the script object "obj" inv.invokeMethod(obj, "hello", "Script Method !!" ); } }
有些時候透過腳本函數或方法可以很方便的實作java接口,而不是在Java中呼叫。同時,透過介面我們可以避免在很多地方使用javax.script API介面。我們可以得到一個介面實作者物件並將其傳遞給不同的Java api。下面的範例示範了透過腳本實作 java.lang.Runnable介面。
import javax.script.*; public class RunnableImpl { public static void main(String[] args) throws Exception { ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName("JavaScript"); // JavaScript code in a String String script = "function run() { println('run called'); }"; // evaluate script engine.eval(script); Invocable inv = (Invocable) engine; // get Runnable interface object from engine. This interface methods // are implemented by script functions with the matching name. Runnable r = inv.getInterface(Runnable.class); // start a new thread that runs the script implemented // runnable interface Thread th = new Thread(r); th.start(); } }
如果你的脚本语言是基于对象或者面向对象的,可以通过脚本对象的脚本方法来实现Java接口。这避免了不得不调用脚本全局函数的接口方法。脚本对象可以存储接口实现状态。
import javax.script.*; public class RunnableImplObject { public static void main(String[] args) throws Exception { ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName("JavaScript"); // JavaScript code in a String String script = "var obj = new Object(); obj.run = function() { println('run method called'); }"; // evaluate script engine.eval(script); // get script object on which we want to implement the interface with Object obj = engine.get("obj"); Invocable inv = (Invocable) engine; // get Runnable interface object from engine. This interface methods // are implemented by script methods of object 'obj' Runnable r = inv.getInterface(obj, Runnable.class); // start a new thread that runs the script implemented // runnable interface Thread th = new Thread(r); th.start(); } }
在 script variables 例子中,我们看到怎样将应用对象暴露为脚本的全局变量。它有可能暴露为多个全局的作用域 。 单作用域是javax.script.Bindings的实例中
. 这个借口派生至java.util.Map994a833a6ffa28d85b72cb15422c29d6。
scope 键值对的集合,其中键为非空、非空字符串。 多scopes 是 javax.script.ScriptContext
接口支持的。支持一个或多个脚本上下文与相关的域绑定。默认情况下, 每一个脚本引擎都有一个默认的脚本上下文。 默认的脚本上下文有至少一个域叫 ”ENGINE_SCOPE”。不同域的脚本上下文支持可以通过 getscopes 方法获取。
import javax.script.*; public class MultiScopes { public static void main(String[] args) throws Exception { ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName("JavaScript"); engine.put("x", "hello"); // print global variable "x" engine.eval("println(x);"); // the above line prints "hello" // Now, pass a different script context ScriptContext newContext = new SimpleScriptContext(); Bindings engineScope = newContext.getBindings(ScriptContext.ENGINE_SCOPE); // add new variable "x" to the new engineScope engineScope.put("x", "world"); // execute the same script - but this time pass a different script context engine.eval("println(x);", newContext); // the above line prints "world" } }
Sun的JDK 6中包含了一个基于 Mozilla Rhino JavaScript 脚本引擎。 这个引擎是基于版本为1.6R2的Mozilla Rhino 。多数 Rhino 实现都被包含在内。少部分组件由于大小和安全原因被排除了:
JavaScript转字节码编译 (也称 ”优化器”).。此功能依赖一个类生成库。 去掉本功能意味着:JavaScript是解释执行,且不影响脚本执行,因为优化器是透明的。
Rhino的JavaAdapter 也被去掉了。 JavaAdapter是一个JavaScript可扩展Java类和JavaScript可实现Java接口功能。此功能也是需要类生成库的。我们把Rhino的JavaAdapter替换为Sun实现的JavaAdapter。在Sun的实现中,仅仅实现了JavaScript对象可实现Java单接口功能。例如,下面的代码会正确执行。
var v = new java.lang.Runnable() { run: function() { print('hello'); } } v.run();
在大多数情况下,JavaAdapter是采用匿名类语法来实现单接口。 使用JavaAdapter来扩展Java类或实现多接口并不常见。
E4X (ECMAScript for XML – ECMA Standard 357) 被去掉了. 使用XML JavaScript代码会产生一个语法错误. 请注意,E4X支持ECMAScript标准是可选的-省略E4X的实现是被支持也是兼容 ECMAScript 。
Rhino的命令行工具 (Rhino shell, debugger 等) 没有被包含在内。但你可以用使用 jrunscript来代替。
在大多数情况下,访问Java类、对象和方法很简单。从JavaScript中访问属性和方法与同Java中一样。这里,我们突出JavaScript Java访问的重要方面.。下面是一些JavaScript访问Java的代码片段。本节需要一些JavaScript知识。如果你打算使用JSR-223中非JavaScript脚本语言,那么本节可以跳过。
内置的函数importPackage
和importClass
可以用于引入Java 包和类。
// Import Java packages and classes // like import package.*; in Java importPackage(java.awt); // like import java.awt.Frame in Java importClass(java.awt.Frame); // Create Java Objects by "new ClassName" var frame = new java.awt.Frame("hello"); // Call Java public methods from script frame.setVisible(true); // Access "JavaBean" properties like "fields" print(frame.title);
全局变量Packages也可以用于访问Java包。例如: Packages.java.util.Vector
, Packages.javax.swing.JFrame
. 请注意: ”java” 是 “Packages.java”的快捷引用。还有一些等价的快捷引用前缀 : javax, org, edu, com, net, 所以几乎所有的 JDK 平台下的类都可以不使用”Packages” 前缀而访问到。
请注意,java.lang不是默认引入的 (与Java不同),因为会与 JavaScript’s 内置的 Object, Boolean, Math 等冲突。
importPackage
和importClass
函数”污染” 了JavaScript中的全局变量。为了避免这种情况,你可以使用JavaImporter。
// create JavaImporter with specific packages and classes to import var SwingGui = new JavaImporter(javax.swing, javax.swing.event, javax.swing.border, java.awt.event); with (SwingGui) { // within this 'with' statement, we can access Swing and AWT // classes by unqualified (simple) names. var mybutton = new JButton("test"); var myframe = new JFrame("test"); }
在JavaScript中,创建一个对象时与Java中一样,而创建Java数组时需要显式的使用Java反射。但一旦创建好后,访问其中的元素或获取大小就和Java中一样。 另外,也可以使用脚本数组用在Java方法中期望的Java数组(因为可以自动转换)。所以在大多数情况下我们不需要显式地创建Java数组。
// create Java String array of 5 elements var a = java.lang.reflect.Array.newInstance(java.lang.String, 5); // Accessing elements and length access is by usual Java syntax a[0] = "scripting is great!"; print(a.length);
在JavaScript中,可以使用Java匿名类语法形式实现Java中接口:
var r = new java.lang.Runnable() { run: function() { print("running...\n"); } }; // "r" can be passed to Java methods that expect java.lang.Runnable var th = new java.lang.Thread(r); th.start();
当接口中只有一个需要实现的方法时,你可以自己传入脚本的函数(因为可以自动转换)。
function func() { print("I am func!"); } // pass script function for java.lang.Runnable argument var th = new java.lang.Thread(func); th.start();
Java方法是使用参数类型重载的。在Java中,重载发生在编译阶段 (执行 javac)。当脚本中调用Java方法时,脚本的翻译器或编译器需要选择适当的方法。对于JavaScript引擎,您不需要做任何特别的——正确的Java方法重载变体是根据参数类型选择的。 但有时,您可能希望(或有)显式地选择一个特定的过载变体。
var out = java.lang.System.out; // select a particular println function out["println(java.lang.Object)"]("hello");
我们不会覆盖的JSR-223兼容脚本引擎实现细节. 至少, 您需要实现javax.script.ScriptEngine
和javax.script.ScriptEngineFactory
接口。 抽象类javax.script.AbstractScriptEngine
提供了一些ScriptEngine
接口中定义的方法。
在开始实现 JSR-223 引擎之前,您可能需要下载。这个工程维护了一些流行的开源脚本语言的 JSR-223 实现。
以上是Java腳本化程式指南的程式碼實例分享的詳細內容。更多資訊請關注PHP中文網其他相關文章!