Java SE 6最引人注目的新功能之一就是內嵌了腳本支持。在默認狀況下,Java SE 6只支持JavaScript,但這並不覺得着Java SE 6只能支持JavaScript。在Java SE 6中提供了一些接口來定義一個腳本規範,也就是JSR223。經過實現這些接口,Java SE 6能夠支持任意的腳本語言(如PHP或Ruby)。javascript
運行第一個腳本程序java
在使用Java SE 6運行腳本以前,必需要知道你的Java SE 6支持什麼腳本語言。在javax.script包中有不少的類,但這些類中最主要的是ScriptEngineManager。能夠經過這個類獲得當前 Java SE 6所支持的全部腳本。以下面例子將列出全部可使用的腳本引擎工廠。多線程
import javax.script.*; import java.io.*; import java.util.*; import static java.lang.System.*; public class ListScriptEngines { public static void main(String args[]){ ScriptEngineManager manager = new ScriptEngineManager(); // 獲得全部的腳本引擎工廠 List factories = manager.getEngineFactories(); // 這是Java SE 5 和Java SE 6的新For語句語法 for (ScriptEngineFactory factory: factories){ // 打印腳本信息 out.printf("Name: %s%n" + "Version: %s%n" + "Language name: %s%n" + "Language version: %s%n" + "Extensions: %s%n" + "Mime types: %s%n" + "Names: %s%n", factory.getEngineName(), factory.getEngineVersion(), factory.getLanguageName(), factory.getLanguageVersion(), factory.getExtensions(), factory.getMimeTypes(), factory.getNames()); // 獲得當前的腳本引擎 ScriptEngine engine = factory.getScriptEngine(); } } }
上面的例子必需要在Java SE 6中編譯。其中import static java.lang.System.*是新的語法,將System中的全部靜態成員進行引用,之後就能夠直接使用out、in或err了。app
經過運行java ListScriptEngines,將顯示以下信息ecmascript
Name: Mozilla Rhino Version: 1.6 release 2 Language name: ECMAScript Language version: 1.6 Extensions: [js] Mime types: [application/javascript, application/ecmascript, text/javascript, text/ecmascript] Names: [js, rhino, JavaScript, javascript, ECMAScript, ecmascript]
在最下面一行是腳本的別名,也就是使用它們中的任意一個均可以。獲得一個具體的腳本引擎有3種方法。異步
ScriptEngine engine = manager.getEngineByExtension("js");
getEngineByExtension的參數就是Extensions:[js]中[…]裏的部分。函數
ScriptEngine engine = manager.getEngineByMimeType("text/javascript");
getEngineByMimeType的參數能夠是Mime types: [application/javascript, application/ecmascript, text/javascript,this
text/ecmascript]中的任何一個,能夠將text/javascript改爲text/ecmascript。spa
ScriptEngine engine = manager.getEngineByName("javascript");
getEngineByName後的參數能夠是Names: [js, rhino, JavaScript, javascript, ECMAScript, ecmascript]中的任何一個,線程
如能夠將javascript改爲ecmascript。
============================================================================================
上面已經討論了執行腳本的第一步,就是獲得一個可用的腳本引擎。在完成這項工做以後就能夠利用這個腳本引擎執行相應的腳本了。咱們可使用ScriptEngine的eval方法來執行腳本。eval方法被重載的屢次,但最經常使用的是 public Object eval(String script)。
下面的例子演示瞭如何使用eval方法來執行javascript腳本。
import javax.script.*; import java.io.*; import static java.lang.System.*; public class FirstJavaScript { public static void main(String args[]) { ScriptEngineManager manager = new ScriptEngineManager(); // 獲得javascript腳本引擎 ScriptEngine engine = manager.getEngineByName("javascript"); try { // 開始運行腳本,並返回當前的小時 Double hour = (Double)engine.eval("var date = new Date();" +"date.getHours();"); String msg; // 將小時轉換爲問候信息 if (hour < 10) { msg = "上午好"; } else if (hour < 16) { msg = "下午好"; } else if (hour < 20) { msg = "晚上好"; } else { msg = "晚安"; } out.printf("小時%s: %s%n", hour, msg); } catch (ScriptException e) { err.println(e); } } }
上面的例子經過獲得當前的小時,並將其轉化爲問候語。上面的程序的輸出信息爲:
小時9.0:上午好
這個例子最值得注意的是執行的2句腳本,最後一句是date.getHours()。並未將這個值賦給一個javascript變量。這時,eval方法就將這樣的值返回。這有些相似C語言的(…)運算符。如(c=a+b, c + d),這個表達式的返回值是a+b+d。
=======================================================================================
和腳本語言進行交互
上面例子只是運行了一個很是簡單的腳本。這個腳本是孤立的,並未經過Java向這腳本傳遞任何的值。雖然從這個腳本返回了一個值,但這種返回方式是隱式的。
腳本引擎除了這些簡單的功能,還爲咱們提供了更強大的功能。甚至能夠經過Java向腳本語言中傳遞參數,還能夠將腳本語言中的變量的值取出來。這些功能要依靠ScriptEngine中的兩個方法put和get。
put 有兩個參數,一個是腳本變量名,另外一個是變量的值,這個值是Object類型,所以,能夠傳遞任何值。
get 有一個參數,就是腳本變量的名。
下面的代碼經過javascript腳本將一個字符串翻轉(這個字符串是經過java傳給javascript的),而後經過java獲得這個被翻轉後的字符後,而後輸出。
import javax.script.*; import java.io.*; import static java.lang.System.*; public class ReverseString { public static void main(String args[]) { ScriptEngineManager manager = new ScriptEngineManager(); // 創建javascript腳本引擎 ScriptEngine engine = manager.getEngineByName("javascript"); try { // 將變量name和變量值abcdefg傳給javascript腳本 engine.put("name", "abcdefg"); // 開始執行腳本 engine.eval("var output ='' ;" + "for (i = 0; i <= name.length; i++) {" + " output = name.charAt(i) + output" + "}"); // 獲得output變量的值 String name = (String)engine.get("output"); out.printf("被翻轉後的字符串:%s", name); } catch (ScriptException e) { err.println(e); } } }
以上代碼的輸出結果爲:
被翻轉後的字符串:gfedcba
==========================================================================================================================
讓腳本運行得更快
衆所周知,解釋運行方式是最慢的運行方式。上述的幾個例子無一例外地都是以解釋方式運行的。因爲Java EE 6的腳本引擎能夠支持任何實現腳本引擎接口的語言。有不少這樣的語言提供了編譯功能,也就是說,在運行腳本以前要先將這些腳本進行編譯(這裏的編譯通常將不是生成可執行文件,而只是在內存中編譯成更容易運行的方式),而後再執行。若是某段腳本要運行之交屢次的話,使用這種方式是很是快的。咱們可使用 ScriptEngine的compile方法進行編譯。並非全部腳本引擎都支持編譯,只有實現了Compilable接口的腳本引擎纔可使用 compile進行編譯,不然將拋出一個錯誤。下面的例子將演示如何使用compile方法編譯並運行javascript腳本。
import javax.script.*; import java.io.*; import static java.lang.System.*; public class CompileScript { public static void main(String args[]) { ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName("javascript"); engine.put("counter", 0); // 向javascript傳遞一個參數 // 判斷這個腳本引擎是否支持編譯功能 if (engine instanceof Compilable) { Compilable compEngine = (Compilable)engine; try { // 進行編譯 CompiledScript script = compEngine.compile("function count() { " + " counter = counter +1; " + " return counter; " + "}; count();"); out.printf("Counter: %s%n", script.eval()); out.printf("Counter: %s%n", script.eval()); out.printf("Counter: %s%n", script.eval()); } catch (ScriptException e) { err.println(e); } } else { err.println("這個腳本引擎不支持編譯!"); } } }
上面的代碼運行後的顯示信息以下:
Counter: 1.0 Counter: 2.0 Counter: 3.0
在這個例子中,先經過compile方法將腳本編譯,而後經過eval方法屢次進行調用。在這段代碼中只有一個函數,所以,eval就返回了這個函數的值。
=========================================================================================================================
動態調用腳本語言的方法
上面的例子只有一個函數,能夠經過eval進行調用並將它的值返回。但若是腳本中有多個函數或想經過用戶的輸入來決定調用哪一個函數,這就須要使用invoke方法進行動態調用。和編譯同樣,腳本引擎必須實現Invocable接口才能夠動態調用腳本語言中的方法。下面的例子將演示如何經過動態調用的方式來運行上面的翻轉字符串的javascript腳本。
import javax.script.*; import java.io.*; import static java.lang.System.*; public class InvocableTest { public static void main(String args[]) { ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName("javascript"); String name="abcdefg"; if (engine instanceof Invocable) { try { engine.eval("function reverse(name) {" + " var output =' ';" + " for (i = 0; i <= name.length; i++) {" + " output = name.charAt(i) + output" + " } return output;}"); Invocable invokeEngine = (Invocable)engine; Object o = invokeEngine.invokeFunction("reverse", name); out.printf("翻轉後的字符串:%s", o); } catch (NoSuchMethodException e) { err.println(e); } catch (ScriptException e) { err.println(e); } } else { err.println("這個腳本引擎不支持動態調用"); } }
動態實現接口
腳本引擎還有一個更吸引的功能,那就是動態實現接口。如咱們要想讓腳本異步地執行,即經過多線程來執行,那InvokeEngine類必須實現 Runnable接口才能夠經過Thread啓動多線程。所以,能夠經過getInterface方法來使InvokeEngine動態地實現 Runnable接口。這樣通常可分爲3步進行。
1. 使用javascript編寫一個run函數
engine.eval("function run() {print(異步執行);}");
2. 經過getInterface方法實現Runnable接口
Runnable runner = invokeEngine.getInterface(Runnable.class);
3. 使用Thread類啓動多線程
Thread t = new Thread(runner);
t.start();
下面是實現這個功能的詳細代碼。
import javax.script.*; import static java.lang.System.*; public class InterfaceTest { public static void main(String args[]) { ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName("javascript"); try { engine.eval("function run() {print(異步調用);}"); Invocable invokeEngine = (Invocable)engine; Runnable runner = invokeEngine.getInterface(Runnable.class); Thread t = new Thread(runner); t.start(); t.join(); } catch (InterruptedException e) { err.println(e); } catch (ScriptException e) { System.err.println(e); } } }
其實上面的代碼是經過javascript實現了Runnable接口的run方法。