2007年4月14日 星期六

純種Java與JavaScript的結合

JavaScript是個廣泛應用的描述性語言,除了製作動態網頁外,常常可以看到各種工具使用各種Script來讓使用者有更彈性的設定,例如:


  • 報表工具(如JasperReport)可以使用用Script自定欄位的顯示格式。

  • 各種Workflow、BPM的工具也可以使用Script訂定各站過站時的邏輯。

  • 在ERP中可以使用Script來設定成本計算公式。

  • 在MES中可以使用Script來設定良率計算公式。




將Script整合進應用程式所能達到的彈性空間的確十分強大,如果能在我們開發的系統也提供Scripting的能力,除了強化擴充性之外,整個系統的質感也是提升不少…爽度up!up!

但以往要達成這目標,有個難處,就是解釋並執行Script用的Interpreting Engine那裡來,自己寫顯然是太累了…。

Java 6這回帶來了好消息:Scripting API,在Java系統中結合Scripting再也不難了,你只需要import javax.script.*;

不可免俗的,還是讓我們來Hello一下吧:


import javax.script.*;

class HelloScript {
public static void main(String args[])
{
ScriptEngineManager mgr = new ScriptEngineManager();
ScriptEngine jsEngine = mgr.getEngineByName("JavaScript");
try {
jsEngine.eval("print('Hello, world!')");
} catch (ScriptException ex) {
ex.printStackTrace();
}
}
}


是的…我在Java程式中使用JavaScript印出了Hello, world!
只需要幾行而已,取得ScriptEngine,然後將要執行的Script傳入eval()中就好了。

不過,通常要讓Script在系統中派上用場,你需要更完全的整合:在Script中要能使用Java物件、在Script中要能使用Java API、Script的執行結果也要可以回報給Java、在Java中也要可以呼叫Script的method。

接著,讓我們來一一擊破…

1. 在Script中使用Java物件

// 先在Java中產生一個ArrayList
List namesList = new ArrayList();
namesList.add("Jill");
namesList.add("Bob");
namesList.add("Laureen");
namesList.add("Ed");

// 取得Script Engine
ScriptEngineManager mgr = new ScriptEngineManager();
ScriptEngine jsEngine = mgr.getEngineByName("JavaScript");

// 把ArrayList物件透過Engine傳進去,取名叫namesListFromJava
jsEngine.put("namesListFromJava", namesList);
System.out.println("==== Executing in script environment... ====");
try {
// 執行Script,注意,此時已經可以在Script中使用namesListFromJava這個
// 由Java傳入的物件了
jsEngine.eval("var x;" +
"var names = namesListFromJava.toArray();" +
"for(x in names) {" +
" println(names[x]);" +
"}" +
"namesListFromJava.add(\"Dana\");");
} catch (ScriptException ex) {
ex.printStackTrace();
}



程式執行結果如下

==== Executing in script environment... ====
Jill
Bob
Laureen
Ed



2. 使用Java呼叫JavaScript Method,並取得其回傳值

// getting engine
ScriptEngineManager mgr = new ScriptEngineManager();
ScriptEngine jsEngine = mgr.getEngineByName("JavaScript");

// 必須將Engine轉型成Invocable
Invocable invocableEngine = (Invocable)jsEngine;

try {
jsEngine.eval("function sumTwoValue(var1,var2) {" +
" return var1 + var2;" +
"}");


// 在Java中呼叫Script Method並印出回傳值
System.out.println("return value from javascript is : " +
invocableEngine.invokeFunction("sumTwoValue", 3, 4));

} catch (ScriptException ex) {
ex.printStackTrace();
} catch (NoSuchMethodException ex) {
ex.printStackTrace();
}




執行結果如下

return value from javascript is : 7.0


3. 在Script中使用Java API

// getting engine
ScriptEngineManager mgr = new ScriptEngineManager();
ScriptEngine jsEngine = mgr.getEngineByName("JavaScript");

try {
jsEngine.eval(
"importPackage(javax.swing);" +
"var optionPane = " +
" JOptionPane.showMessageDialog(null, 'Hello, world!');");
} catch (ScriptException ex) {
ex.printStackTrace();
}



程式執行結果如下:


可以發現,我輕鬆的在JavaScript中叫出了一個Swing對話盒,向各位說「Hello, world!」。

JDK6 野馬與Script的整合,的確讓牠更狂野奔放了:D

3 則留言:

好網站推薦 提到...

請問可以在script中建立JDBC的連結嗎?
想要查詢資料庫

我是一隻Coding Monkey 提到...

可以,但是比較麻煩,建議你寫一個class,把資料庫存取的功能包裝起來,然後在Script中使用這個class。

提到...

請問有可能用 javascript 去 parse 網頁的資料並且回傳 DOM 相關的資訊回來給 java 嗎? (會這樣想, 是因為 javascipt parse HTML 出來的 DOM tree 與 java 用外加的 parser 取得的 DOM tree 並不依樣).