2012年10月30日 星期二

範例 - JavaScript 等比例縮小圖片

max-width、max-height以及這兩個的 min 版本是偶爾能在某些CSS中看到的屬性設定
不過因為IE不會辨識這些屬性,所以就直接寫JavaScript 調整圖片大小

var img = this.getElementsByTagName("img")[0];

img.onload = function(){
    var maxWidth = "300", maxHeight = "80";
 
    if(img.offsetHeight >= (maxHeight / maxWidth) * img.offsetWidth){
    img.style.width = img.offsetWidth / img.offsetHeight * maxHeight + "px";
    img.style.height = maxHeight + "px";
    }else{
img.style.height = img.offsetHeight / img.offsetWidth * maxHeight + "px";
img.style.width = maxWidth + "px";
    }
}

轉換AJAX取得的XML結構為DOM操作結構

這次的目的是要用AJAX取得的 responseText ,作為節點底下的結構
取得的 responseText 會是 <div>....</div><div>...</div> 這樣的結構
再以DOM方式去操作

通常查詢到的都是用framework的解決方式
例如JQuery - $("...").html( responseText );
Pototype.js也有update() method可以滿足這類需求

不過...沒看到用native JavaScript的過程
想想也是啦...畢竟這樣應該就是要自己去解析字串了
查到較相近的是XML結構的操作
雖然不符合我的需求,但還是值得記錄起來

xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET","rss.xml",false);
xmlhttp.send();
xmlText = xmlhttp.responseText;

if (window.DOMParser){
 parser = new DOMParser();
 xmlDoc = parser.parseFromString(xmlText,"text/xml");
} else{
 xmlDoc = new ActiveXObject("Microsoft.XMLDOM");  //for IE
 xmlDoc.loadXML(xmlText);
}

如果是直接取得XML的話用以下形式即可

xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET","rss.xml",false);
xmlhttp.send();
xmlDoc = xmlhttp.responseXML;

2012年10月29日 星期一

Python 基礎-List 介紹 與常見BIF示範

Python保存資料使用的是類似陣列的清單結構
但型別是由Python編譯器處理
Python的List能混合存入各類型的資料,如字串和數字混合放置
另外List中也能包含其他的list,就像多重陣列一樣

Python的多行註解形式是 """註解""" ,單行註解則用#
在編譯器中執行以下程式碼

initList = []         #建立空的List,也可以用BIF方式建立 initList = list()
lists = ["new", "show", "Eclipse"]
demo = ["aa", ["bb", "cc"]]
print(initList)
print(lists[1])    
print(len(lists))     #這裡用上了len() BIF回傳List長度
print(demo[1])      

"""
output:

[]
show
3
['bb', 'cc']
"""

Python稱呼它的內建方法為BIF(built-In Function),常看到的還有

  函式                        說明                                    範例

append()   -   將一筆資料附加到尾端          lists.append("lol")
pop()        -   從最尾端取出一筆資料          lists.pop()           #output:lol
extend()    -   加入一群資料                       lists.extend(["hi", "hello"])
remove()   -   移除特定的資料項                 lists.remove("hi")
insert()      -   插入一筆資料                       lists.insert(0, "head")
list()         -   用於新建空清單的函式
len()         -   取得List長度                         len(List)
range()     -    回傳一個Iterator                    for num in range(4):  #output:0 1 2 3
                   並依照給定的範圍產生數字     print(num)
sort()       -   將List排序,由低到高
reverse()   -  逆轉陣列的順序
count()     -   回傳某個值在List中出現的次數

sum()      -   會將引數中的List數字作加總        sum(list)
randint()   -   前回介於兩引數之間的任意整數      randint(1, 10)
int()        -   將一個字串或數字轉換為整數
float()      -   將一個字串或數字轉換為浮點數
str()        -   將數字轉換成字串
id()         -   傳回一個資料物件在Python中的獨特編號
input()     -   取得並回傳使用者輸入

enumerate() - 補足Iterator的index值,使用方法如下:
           for i, n in enumerate([1, 3, 5]):
       print(i, n)

"""  output
0 1
   1 3
   2 5
"""

zip()     -   整合多個Lists,使用方法如下:
          names = ["John", "Marry", "Tom"]
          sexes = ["Male", "Female", "Male"]
          for name, sex in zip(names, sexes):
              print(name, sex)

"""  output
John Male
   Marry Female
                        Tom Male
"""

Python 安裝配置與 Eclipse PyDev 設定

參考文章:Python: Eclipse+Pydev環境安裝教學
                 在 Windows 7 環境安裝 Python 2.6.6

本篇文章介紹 Python 3 在 Windows 環境的安裝配置過程
另外開發過程會以PyDev作為IDE

首先到Python官網下載需要的版本
安裝後到 [電腦] -> 右鍵[內容] -> [進階系統設定] -> [環境變數]  中添加環境變數
環境變數的設定過程類似 Java


PYTHON_HOME :C:\Python33
PATH: ;%PYTHON_HOME%    (附加在現有內容後)


新建立PYTHON_HOME變數後,在PATH變數加上上述內容就完成了
要確認的話可以到 cmd (命令提示字元) 中輸入 Python 看看

Python雖然可以多個版本並存,但能設定在PATH裡的只能有一個版本
設定主要開發版本即可

Python本身有自帶 IDLE 可以做為簡單版的編譯器使用
它有幾個簡單的快捷鍵:

TAB鍵可以提示尚未輸入完的程式碼
Alt + P 可以召回前一組輸入的程式碼
Alt + N 移往下一個程式碼


不過因為Python是個極端重視縮排的語言
往往縮排就可以讓人在除錯過程中搞得要死不活
建議還是用IDE進行開發
個人因常使用Eclipse而選擇用其平台的PyDev

在Eclipse中安裝 plugin
[Help] -> [Install New software]  加入
name: Pydev
url: http://pydev.org/updates

就個人遇過的情形 Aptana 跟 Pydev 的安裝會有衝突
因為兩者套件都各自有引用對方的部分
最後我是只選擇安裝PyDev

安裝完後到 [Window] –> [PyDev] -> [Interpreter – Python] 設定Python編譯器的路徑
加入 Python 安裝目錄底下的所有東西就好了
之後開發時選擇Python專案就可以開始開發Python了

2012年10月24日 星期三

Java 數值轉換函式區別 - 以Integer.parseInt()、Integer.valueOf()為例

之前對於Java在數值轉換中為什麼會提供這兩種功能幾乎一樣的方法感到疑惑
看了原始碼就可以得到解答
public static Integer valueOf(String s) throws NumberFormatException {
  return new Integer(parseInt(s, 10));
}

public static int parseInt(String s) throws NumberFormatException {
  return parseInt(s,10);
}

答案在於回傳型別的不同
parseInt()回傳 int 基本型別,valueOf()則回傳Integer型別
除了Integer外,其他的數值型別也有同樣的方法(Double、Float...etc)

在Java1.4版本以前這兩種方法有差異,但在後來版本裡因為有auto boxing機制而沒差了

2012年10月22日 星期一

JavaScript 少用全域變數的理由

在 JavaScript: The Good Parts 書中即有討論到這個議題(心得文連結)
容易造成變數名稱紊亂這點,可以盡量用匿名函式的寫法解決
在匿名函式中使用的變數名稱就不會跟全域變數產生衝突
但除了這個問題外,還有效能的考量
JavaScript搜尋變數的方式是 區域變數 -> 更上層的區域變數 -> .... -> 全域變數
若有常用到的全域變數,把它另存成區域變數對效能有很大的幫助

function displayData(){
  //caching variables
  var _ctrl=ctrl;
  var _nui=nui;
  ....
}

Excel 開啟產出的 .CSV檔為亂碼的解決方式

CSV檔為以逗號區隔欄位,用來儲存資料的純文字格式
指定了編碼的純文字格式應該是與亂碼無緣
但有時以微軟的Excel開啟.csv檔卻會發現內容為亂碼
發生的原因是微軟內部系統處理格式時會在開頭暗中寫入一串判別的字碼
而微軟系統就以文字檔案中是否含這串暗碼來判定要以UTF-8或ASCII格式讀取檔案

所以我們若要讓我們產出的CSV檔能正確的被Excel讀取,就要在檔案開頭加入暗碼


public CSVWriter(OutputStream out){
    this.out=out;

    //Corresponding to Microsoft Excel, make file won't open with scrambles in Excel
    byte[] BOM_UTF8 = { (byte) 0xEF, (byte) 0xBB, (byte) 0xBF };
    try {
    out.write(BOM_UTF8);
    } catch (IOException e) {
    System.err.println("BOM寫入CSV失敗");
    }
    ....
}

2012年10月16日 星期二

IE 中checkbox的checked=true不打勾問題解決


這個問題是發現在IE中會沒辦法看到checkbox的選項被打勾
檢查其屬性checked=true無誤

追查下發現原因為在該checkbox尚未被IE初始化(還沒加入DOM樹中)前設定此屬性

解決方法有兩種 -
1.將其加入DOM樹中再設定屬性
2.改為設定其 defaultChecked=true

2012年10月15日 星期一

動態加入CSS 與 IE 的 innerHTML 屬性討論

這幾天有遇到一個情況是動態加入CSS時發現在IE會遇到問題
查過後才發現 IE 會限制部分Element的 innerHTML 屬性值的設定
col, colgroup, frameset, html, style, table, tbody, tfoot, thead, title, tr
上述標籤的innerTML attribute 在 IE 中均為唯讀

以下有兩種處理方式可通用在所有瀏覽器中

1.
....
    var se = document.getElementById("ieStyleApplier");
    se.innerHTML = "";
    se.innerHTML = "_<style>" + o.responseText + "</style>";
...

先設定一個空的div element
再再將CSS內容將入到div的範圍內用(本例中的o.responseText 為Ajax取得CSS內容)
注意stye tag 前加入 '_' 作為前綴字元(prefix character)
這是避免IE解析div element時讀到style tag會停止解析的情形
猜測這是IE的安全性考量
加入前綴字元後就沒問題了,也可以對應到其他的browser

2.

  (function(o){
      var newStyle = document.createElement("link");
      newStyle.rel = "stylesheet";
      newStyle.href = o;
      var head = document.getElementsByTagName('head')[0];
      if(head)
          head.appendChild(newStyle);
      else
          document.body.appendChild(newStyle);
  })(url);

用一次性的函式動態加入link element後引入CSS

2012年10月8日 星期一

跨網域資料取得解決方式 - JSONP

參考文章:【原創】說說JSON和JSONP,也許你會豁然開朗,含jQuery用例
                      使用 JSONP 跨站請求

JSON為JavaScript中常用的資料交換格式
簡單、輕型的特性讓他成為Web 開發的首選,這裡就不多提了

而在Web開發的過程中,我們有時會遇到跨網域存取資料的需求
不管是基於分散流量需求,又或者是使用雲端提供業者的服務
使用第三方提供的服務、資源
甚至在同一個專案目錄底下開發的檔案也可能因啟用HTTPS而導致網域的不同

我們有這麼多會碰上跨網域的情況
但Browser 本身的安全機制會禁止我們直接從其他網域取得資料
瀏覽器唯一能從其他網域取得資源的機會就是JavaScript的匯入
也因此衍伸出一個非官方的解決方案,也就是JSONP
JSONP的全名是JSON with Padding
這項技巧本身與JSON沒有實際關係,僅僅是因為JSON是實作上最使用的格式

JSONP簡單的來說即是利用JavaScript的動態載入
將JSON格式的資料讀取進來後,直接在JavaScript做處理
這與 AJAX是不同的技術方式
AJAX的目的是獲取目標頁面的資料,而JSONP則是獲取一段JavaScript程式碼

欲取得資料的頁面寫下類似下面這段程式碼:

    var head = document.getElementsByTagName('head')[0];
    var storeList = null;

//倒入JSON資料到storeList
var JSONDataGetter = function(data){
storeList = JSON.parse(JSON.stringify(data));
};

var src = "firmList.jsp";      //JSON資料提供頁面
var js = document.createElement('script');
sJs.setAttribute('src', src);
    if(head)
        head.appendChild(js);
    else
        document.body.appendChild(js);

上面這段的 JSONDataGetter() 會將傳入的JSON資料轉成JavaScript物件
接著是資料提供者的頁面的程式碼
<%
response.setContentType("text/javascript");  //將頁面的型別設定為JavaScript

....  //JSP code

int counter = 0;
out.print("JSONDataGetter([");
    for(Map.Entry<String, HashMap<String, String>> m : totalMap.entrySet()){
    HashMap<String, String> store = m.getValue();
   
    if(counter == 0){
    out.print(String.format("{ \"storeName\" : \"%s\", \"storeType\" : \"%s\", \"storeLocation\" : \"%s\"}", m.getKey(), store.get("storeType"), store.get("storeLocation")));
    }else{
    out.print(String.format(", { \"storeName\" : \"%s\", \"storeType\" : \"%s\", \"storeLocation\" : \"%s\"}", m.getKey(), store.get("storeType"), store.get("storeLocation")));
    }
    counter++;
    }
    out.print("])");

%>


大部分都是字串拼貼的處理,顯示出來大概就是下面這樣

JSONDataGetter([ //JSON資料])

引入的JavaScript code會回頭呼叫 JSONDataGetter() 處理
實務上也會常看到在引入JavaScript的路徑上加上callback變數回傳
如:var src = "firmList.jsp?callback=XXX";
提供 JSON 資料的頁面再根據參數而輸出不同的字串,以便呼叫不同的處理函式
如此一來就能做到更多元的處理
這就是JSONP了

2012年10月3日 星期三

[轉載] POI設置EXCEL單元格格式為文本、小數、百分比、貨幣、日期、科學計數法和中文大寫

轉載來源


以下將要介紹的每一種都會用到這三行中的變量

            HSSFWorkbook demoWorkBook = new HSSFWorkbook();  
            HSSFSheet demoSheet = demoWorkBook.createSheet("The World's 500 Enterprises");  
            HSSFCell cell = demoSheet.createRow(0).createCell(0);

第一種:日期格式

            cell.setCellValue(new Date(2008,5,5));
            //set date format
            HSSFCellStyle cellStyle = demoWorkBook.createCellStyle();
            HSSFDataFormat format= demoWorkBook.createDataFormat();
            cellStyle.setDataFormat(format.getFormat("yyyy年m月d日"));
            cell.setCellStyle(cellStyle);

第二種:保留兩位小數格式
            cell.setCellValue(1.2);
            HSSFCellStyle cellStyle = demoWorkBook.createCellStyle();
            cellStyle.setDataFormat(HSSFDataFormat.getBuiltinFormat("0.00"));
            cell.setCellStyle(cellStyle);

這裡與上面有所不同,用的是HSSFDataFormat.getBuiltinFormat()方法,之所以用這個,是因為0.00是Excel內嵌的格式

第三種:貨幣格式

            cell.setCellValue(20000);
            HSSFCellStyle cellStyle = demoWorkBook.createCellStyle();
            HSSFDataFormat format= demoWorkBook.createDataFormat();
            cellStyle.setDataFormat(format.getFormat("¥#,##0"));
            cell.setCellStyle(cellStyle);

第四種:百分比格式

            cell.setCellValue(20);
            HSSFCellStyle cellStyle = demoWorkBook.createCellStyle();
            cellStyle.setDataFormat(HSSFDataFormat.getBuiltinFormat("0.00%"));
            cell.setCellStyle(cellStyle);
  此種情況跟第二種一樣

第五種:中文大寫格式

            cell.setCellValue(20000);
            HSSFCellStyle cellStyle = demoWorkBook.createCellStyle();
            HSSFDataFormat format= demoWorkBook.createDataFormat();
            cellStyle.setDataFormat(format.getFormat("[DbNum2][$-804]0"));
            cell.setCellStyle(cellStyle);

第六種:科學計數法格式

            cell.setCellValue(20000);
            HSSFCellStyle cellStyle = demoWorkBook.createCellStyle();
            cellStyle.setDataFormat( HSSFDataFormat.getBuiltinFormat("0.00E+00"));
            cell.setCellStyle(cellStyle);
此種情況也與第二種情況一樣

2012年10月2日 星期二

[轉載] Java SE 6.0 內建 Javascript 引擎測試

轉載來源

前言 :
Java SE 6.0 採納 JSR 223 建議,使得 Java 能夠與眾多 Script 語言協同運作
透過 Java 定義的標準 API,能夠讓 Script 直接存取和控制 Java 的物件
而Java 應用程式當中也能夠直接內嵌 Script 環境

範例說明 :
底下將會帶幾個範例, 來說明這個 Java 的 JavaScript 引擎能夠達成的工作 :
- 範例01 : 第一個 Hello World 程式, 直接將 JavaScript 代碼丟入 JS Engine 的 eval 函式執行.
package john.js;

import javax.script.*;

public class JSHello {
    public static void main(String args[]) {
        ScriptEngineManager mgr = new ScriptEngineManager(); // 建立一個 Script 管理員
        ScriptEngine jsEngine = mgr.getEngineByName("JavaScript"); // 取得 Javascript 引擎
        try {
          jsEngine.eval("print('Hello, world!')"); // 透過 Javascript 引擎解譯 print('hello, world') 命令
        } catch (ScriptException ex) {
            ex.printStackTrace();
        }    
    }
}

- 範例02:執行外部 javasript 檔案, 這裡是直接把含 JavaScript 代碼(Hello.js) 的內容透過 InputStream 丟給 JS Engine 的 eval API 執行.
package john.js;

import javax.script.*;
import java.io.*;

public class JSRunExtendFile {
    public JSRunExtendFile() {
        ScriptEngineManager engineMgr = new ScriptEngineManager();
        ScriptEngine engine = engineMgr.getEngineByName("ECMAScript");      
        try {
            InputStream is = new FileInputStream(new File("Hello.js"));
            Reader reader = new InputStreamReader(is);
            engine.eval(reader);
        } catch (ScriptException ex) {
            ex.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        JSRunExtendFile ref = new JSRunExtendFile();  
    }
}

- 範例03:透過 Invocable 介面喚起 Javascript 函數.
package john.js;

import javax.script.*;

public class JSInvokeFunction {

    public static void main(String[] args) {
        ScriptEngineManager engineMgr = new ScriptEngineManager();
        ScriptEngine jsEngine = engineMgr.getEngineByName("ECMAScript");
        try {
            jsEngine.eval("function sayHello() {"
                    + "  println('Hello, world!');" + "}");
            Invocable invocableEngine = (Invocable) jsEngine;
            invocableEngine.invokeFunction("sayHello");
        } catch (ScriptException ex) {
            ex.printStackTrace();
        } catch (NoSuchMethodException nsme) {
            nsme.printStackTrace();
        }
    }
}

- 範例04:Script 直接存取 Java 物件
package john.js;

import javax.script.*;
import java.util.*;

public class JSAccessJavaObject {

    public static void main(String[] args) {
        List namesList = new ArrayList();
        namesList.add("Jill");
        namesList.add("Bob");
        namesList.add("Laureen");
        namesList.add("Ed");
        ScriptEngineManager engineMgr = new ScriptEngineManager();
        ScriptEngine jsEngine = engineMgr.getEngineByName("ECMAScript");
        jsEngine.put("namesListKey", namesList); // 將 java 變數指定給 javascript 環境中的
                                                    // nameListKey 變數
        System.out.println("Executing in script environment...");
        try {
            // 列印 Java 的 nameList 變數內容,並新增一個 Dana 字串資料到 List 尾端
            jsEngine.eval("var x;" + "var names = namesListKey.toArray();"
                    + "for(x in names) {" + "  println(names[x]);" + "}"
                    + "namesListKey.add(\"Dana\");");
        } catch (ScriptException ex) {
            ex.printStackTrace();
        }
        System.out.println("Executing in Java environment...");
        for (String name : namesList) {
            System.out.println(name);
        }
    }
}

- 範例05:透過 Invocable 將參數值傳遞給 Javascript 中的函數
package john.js;

import javax.script.*;
import java.util.*;

public class JSPassValueToFunction {

    public static void main(String[] args) {
        List namesList = new ArrayList();
        namesList.add("Jill");
        namesList.add("Bob");
        namesList.add("Laureen");
        namesList.add("Ed");
        ScriptEngineManager engineMgr = new ScriptEngineManager();
        ScriptEngine jsEngine = engineMgr.getEngineByName("ECMAScript");
        Invocable invocableEngine = (Invocable) jsEngine;
        try {
            jsEngine.eval("function printNames1(namesList) {" + "  var x;"
                    + "  var names = namesList.toArray();"
                    + "  for(x in names) {" + "    println(names[x]);" + "  }"
                    + "}" + "function addName(namesList, name) {println('addName: Add '+name);"
                    + "  namesList.add(name);" + "}");
            invocableEngine.invokeFunction("printNames1", namesList);
            invocableEngine.invokeFunction("addName", namesList, "Dana");
        } catch (ScriptException ex) {
            ex.printStackTrace();
        } catch (NoSuchMethodException ex) {
            ex.printStackTrace();
        }
    }
}

補充說明 :
* 純種 Java 與 Javascript 結合
將Script整合進應用程式所能達到的彈性空間的確十分強大
如果能在我們開發的系統也提供Scripting的能力,除了強化擴充性之外,整個系統的質感也是提升不少…爽度up!up!
但以往要達成這目標,有個難處,就是解釋並執行Script用的Interpreting Engine那裡來,自己寫顯然是太累了…。
Java 6這回帶來了好消息:Scripting API,在Java系統中結合Scripting再也不難了,你只需要import javax.script.*; ...

[轉載] javax.script包探秘 javascript與java交互執行

轉載來源

這個包用來和JavaScript進行互操作,比如JAVA類可以調用JavaScript中的方法,而JS也可調用 JAVA中的方法.

1、可用的腳本引擎
Java 6提供對執行腳本語言的支持,這個支持來自於JSR223規範,對應的包是javax.script
預設情況下,Java 6只支持javascript腳本,它底層的實現是Mozilla Rhino,它是個純Java的javascript實現
可以通過下面的代碼列出當前環境中支持的腳本引擎:

// 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('Welocme to java world')");
System.out.println();
ScriptEngineManager manager = new ScriptEngineManager();
List<ScriptEngineFactory> factories = manager.getEngineFactories();
for (ScriptEngineFactory f : factories) {
    System.out.println("egine name:" + f.getEngineName() + "\n"
            + "engine version:" + f.getEngineVersion() + "\n"
            + "language name:" + f.getLanguageName());
}

結果輸出:
Welocme to java world
egine name:Mozilla Rhino
engine version:1.6 release 2
language name:ECMAScript

可以看到,Java內置只支持javascript一種腳本
但是,只要遵循 JSR223,便可以擴展支持多種腳本語言
可以從https://scripting.dev.java.net/上查找當前已被支持的腳本的第三方庫

2、hello script
示例如上面的代碼所示。
使用的API還是很簡單的,ScriptEngineManager是ScriptEngine的工廠,實例化該工廠的時候會加載可用的所有腳本引擎
從工廠中創建ScriptEngine可以使用getEngineByName、getEngineByExtension或 getEngineByMimeType來得到
只要參數名字能對上。執行腳本調用eval方法即可(效果等同於javascript中的eval)

3、傳遞變量
可以向腳本中傳遞變量,使得Java代碼可以和腳本代碼交互,示例如下:

// create a script engine manager
ScriptEngineManager factory = new ScriptEngineManager();
// create a JavaScript engine
ScriptEngine engine = factory.getEngineByName("JavaScript");
engine.put("a", 4);
engine.put("b", 6);
try {
    Object maxNum = engine
            .eval("function max_num(a,b){return (a>b)?a:b;}max_num(a,b);");
    System.out.println("max_num:" + maxNum);
} catch (Exception e) {
    e.printStackTrace();
}
輸出內容:max_num:6

對於上面put的變量,它作用於自身engine範圍內,也就是ScriptContext.ENGINE_SCOPE
put 的變量放到一個叫Bindings的Map中,可以通過 engine.getBindings(ScriptContext.ENGINE_SCOPE).get(「a」);得到put的內容
和 ENGINE_SCOPE相對,還有個ScriptContext.GLOBAL_SCOPE 作用域
其作用的變量是由同一ScriptEngineFactory創建的所有ScriptEngine共享的全局作用域。

4、動態調用
上面的例子中定義了一個javascript函數max_num,可以通過Invocable接口來多次調用腳本庫中的函數
Invocable接口是 ScriptEngine可選實現的接口。下面是個使用示例:

// create a script engine manager
ScriptEngineManager factory = new ScriptEngineManager();
// create a JavaScript engine
ScriptEngine engine = factory.getEngineByName("JavaScript");
try {
    engine.eval("function max_num(a,b){return (a>b)?a:b;}");
    Invocable invoke = (Invocable) engine;
    Object maxNum = invoke.invokeFunction("max_num", 4, 6);
    System.out.println(maxNum);
    maxNum = invoke.invokeFunction("max_num", 7, 6);
    System.out.println(maxNum);
} catch (Exception e) {
    // TODO: handle exception
}

上面的invokeFunction,第一個參數調用的腳本函數名,後面跟的可變參數是對應的腳本函數參數
Invocable還有個很酷的功能,就是動態實現接口,它可以從腳本引擎中得到Java Interface 的實例
也就是說,可以定義個一個Java接口,其實現是由腳本完成
以上面的例子為例,定義接口JSLib,該接口中的函數和javascript中的函數簽名保持一致:

public interface JSLib {
    public int max_num(int a, int b);
}
[java] view plaincopyprint?
// create a script engine manager
ScriptEngineManager factory = new ScriptEngineManager();
// create a JavaScript engine
ScriptEngine engine = factory.getEngineByName("JavaScript");
try {
    engine.eval("function max_num(a,b){return (a>b)?a:b;}");
    Invocable invoke = (Invocable) engine;
    JSLib jslib = invoke.getInterface(JSLib.class);
    int maxNum = jslib.max_num(4, 6);
    System.out.println(maxNum);
} catch (Exception e) {
    // TODO: handle exception
}

5、使用Java對象
可以在javascript中使用Java代碼,這確實是很酷的事情
在Rhino中,可以通過importClass導入一個類,也可以通過importPackage導入一個包,也可以直接使用全路經的類
在創建對象時,new也不是必須的。示例代碼如下:

try {
    String script = "var list = java.util.ArrayList();list.add(\"kafka0102\");print(list.get(0));";
    engine.eval(script);
} catch (Exception e) {
    e.printStackTrace();
}

又例如:

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

6、編譯執行
腳本引擎默認是解釋執行的,如果需要反覆執行腳本,可以使用它的可選接口Compilable來編譯執行腳本,以獲得更好的性能,示例代碼如下:

try {
    Compilable compEngine = (Compilable) engine;
    CompiledScript script = compEngine
            .compile("function max_num(a,b){return (a>b)?a:b;}");
    script.eval();
    Invocable invoke = (Invocable) engine;
    Object maxNum = invoke.invokeFunction("max_num", 4, 6);
    System.out.println(maxNum);
} catch (Exception e) {
    e.printStackTrace();
}

7、總結
除了上面提到的特性,腳本引擎還有一些不錯的功能
比如可以執行腳本文件,可以由多線程異步執行腳本等功能
引入腳本引擎,可以對一些配置擴展和業務規則做更強大而靈活的支持,也方便使用者選擇自己熟悉的腳本語言來編寫業務規則等

public class JSTest {
    public static void main(String[] args) throws Exception {
        // 根據js的後綴名生成一個解析JS的腳本解析引擎

        ScriptEngine engin = new ScriptEngineManager()
                .getEngineByExtension("js");
        // 查詢一下這個引擎是否實現了下面很實用的接口

        System.out.println(engin instanceof Invocable);
        // 聲明兩個對象,傳入到JS裡面去

        JFrame jf = new JFrame("test");
        List list = new ArrayList();
        // 得到綁定的鍵值對象,把當前的兩個JAVA對象放進去

        Bindings bind = engin.createBindings();
        bind.put("jf", jf);
        bind.put("list", list);
        // 把綁定下的鍵值對象放進去,作用域是當前引擎的範圍

        engin.setBindings(bind, ScriptContext.ENGINE_SCOPE);
        // 用引擎執行一段寫在JS文件裡面的代碼

        Object obj = engin.eval(new FileReader("test.js"));
        // 這個時候返回值當然 是null了

        System.out.println(obj);
        // 把當前的引擎強制轉為Invocable,這樣就可以調用定義在JS文件裡面的一個一個函數了

        Invocable in = (Invocable) engin;
        // 得到了從JS裡面返回來的對象

        List l = (List) in.invokeFunction("getNames");
        System.out.println(l);
        // 調用一下定義在JS裡面的另一個函數

        in.invokeFunction("testJS");
        // 最後調用一個函數,該函數可以使我們前面定義的窗體顯示出來

        in.invokeFunction("doSth");
    }
}
下面是定義在test.js裡面的內容
[javascript] view plaincopyprint?
function doSth()
{
    jf.setSize(500,300);
    jf.setVisible(true);
    jf.setDefaultCloseOperation(jf.EXIT_ON_CLOSE);  
}  
function getNames()
{   
    list.add("doSth");
    list.add("getNames");
    return list;
}  
function testJS()
{
    print('Hello world!');
}
我們可以看到,在JAVA運行了以後,窗體會顯示出來,並且我們可以接收到從JS解析引擎裡面傳回的數據
當然我們也可以調用一個很普通的JS函數
想像一下,如果我們把我們程序運行時的一些對象都設到Bindings裡面去,那麼我們JS豈不是有很大的自由度了嗎?
因為JS裡面也可以操作我們的Java對象了,並且我們可以像ava編程一樣的對JS編程了,還不用再編譯,馬上就可以運行.靈活性豈不是變得更高了嗎?

參考自:
【1】javax.script包探秘 javascript與java交互執行
http://blog.csdn.net/lvshow/article/details/7252236
【2】Scripting for the Java Platform
http://java.sun.com/developer/technicalArticles/J2SE/Desktop/scripting/