Java 6 標準化了腳本引擎 API (JSR-223),內建 Mozilla Rhino (JavaScript),並支援 Groovy、JRuby、Jython、PHP 等第三方引擎。
| 類別/介面 | 用途 |
|---|---|
ScriptEngineManager | 引擎工廠,按名稱/副檔名/MIME 獲取引擎 |
ScriptEngine | 核心介面,提供 eval()、put()/get() 變數綁定 |
Bindings | 鍵值對映射,實現 Java↔腳本雙向資料交換 |
Compilable | 支援預編譯腳本為 CompiledScript,大幅提升重複執行效能 |
Invocable | 直接呼叫腳本中定義的函數/方法 |
ScriptEngineManager mgr = new ScriptEngineManager();
ScriptEngine engine = mgr.getEngineByName("js"); // 或 getEngineByExtension("js")
engine.put("age", 21);
engine.eval("if (age >= 18) { print('Old enough to vote!'); } else { print('Back to school!'); }");
// 輸出: Old enough to vote!
engine.eval(new FileReader("rules/pricing.js"));
// 方式 A: eval() 回傳最後一句表達式值
Object result = engine.eval("riskFactor = (age < 25) ? 1.5 : (noClaims ? 0.75 : 1.0);");
// result 為 0.75 (假設 age=26, noClaims=true)
// 方式 B: Bindings 共享狀態
Bindings bindings = engine.createBindings();
bindings.put("age", 26);
bindings.put("noClaims", true);
engine.eval(script, bindings);
double risk = (Double) bindings.get("riskFactor"); // 0.75
engine.put("name", "John Doe");
engine.eval("name2 = name.toUpperCase(); print('Converted: ' + name2);");
// 輸出: Converted: JOHN DOE
// 導入 Java 套件
engine.eval("importPackage(java.util); today = new Date(); print(today);");
Compilable compilable = (Compilable) engine;
CompiledScript script = compilable.compile(scriptText); // 一次編譯
// 多次重用,傳入不同 Bindings
for (int i = 0; i < 10000; i++) {
Bindings b = engine.createBindings();
b.put("age", ages[i]);
b.put("noClaims", flags[i]);
script.eval(b);
}
實測:編譯後 JS 執行速度約為解釋執行的 3 倍,但仍比原生 Java 慢 10 萬倍。適用於「邏輯複雜、頻繁執行、但不追求極致效能」的業務規則場景。
engine.eval("function increment(i) { return i + 1; }");
Invocable inv = (Invocable) engine;
Object result = inv.invokeFunction("increment", 10); // 回傳 11
此特性支援「腳本庫 + Java 調用」架構:精算師維護 JS 定價庫,Java 後台統一調用。
JSR-223 目標之一是讓 PHP/JSP 等非 Java 頁面無縫接入 Servlet 容器:
<%-- PHP 頁面中 --%>
$date = new Java("java.util.Date");
echo $date->toString();
// 訪問 Session
$session = $request->getSession();
$attrs = $session->getAttributeNames();
雖生產環境較少見,但展示了「Java 作為底層平台,多語言共存」的架構想像力。
ScriptEngine 非線程安全,多線程環境下每線程建立實例或使用 ThreadLocal 池化。new InputStreamReader(is, "UTF-8")。JSR-223 + Rhino 為 Java 應用注入了動態性,適合業務規則引擎、用戶腳本擴展、測試腳本等場景。掌握 eval/Bindings/Compilable/Invocable 四大核心模式,即可駕馭腳本與 Java 的雙向互操作。