首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 6 (Mustang) 腳本引擎 JSR-223 實戰:內建 Rhino JavaScript 完整指南

     編輯:Java 相關知識

背景:為什麼 Java 需要腳本引擎?

  • 業務規則外部化:保險費率、促銷邏輯等頻變規則用腳本表達,無需重編譯部署。
  • 用戶自定義擴展:讓終端用戶編寫腳本定製產品行為(如 Excel 公式、遊戲 Mod)。
  • 開發效率:動態語言語法簡潔,適合快速原型、測試腳本、DSL 開發。

Java 6 標準化了腳本引擎 API (JSR-223),內建 Mozilla Rhino (JavaScript),並支援 Groovy、JRuby、Jython、PHP 等第三方引擎。

核心 API 速覽

類別/介面用途
ScriptEngineManager引擎工廠,按名稱/副檔名/MIME 獲取引擎
ScriptEngine核心介面,提供 eval()、put()/get() 變數綁定
Bindings鍵值對映射,實現 Java↔腳本雙向資料交換
Compilable支援預編譯腳本為 CompiledScript,大幅提升重複執行效能
Invocable直接呼叫腳本中定義的函數/方法

快速上手:Hello JavaScript

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!

進階用法詳解

1. 從外部檔案載入腳本

engine.eval(new FileReader("rules/pricing.js"));

2. 獲取執行結果

// 方式 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

3. 腳本中訪問 Java 物件與 API

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);");

4. 編譯腳本提升效能 (Compilable)

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 萬倍。適用於「邏輯複雜、頻繁執行、但不追求極致效能」的業務規則場景。

5. 直接呼叫腳本函數

engine.eval("function increment(i) { return i + 1; }");
Invocable inv = (Invocable) engine;
Object result = inv.invokeFunction("increment", 10); // 回傳 11

此特性支援「腳本庫 + Java 調用」架構:精算師維護 JS 定價庫,Java 後台統一調用。

Web 整合願景

JSR-223 目標之一是讓 PHP/JSP 等非 Java 頁面無縫接入 Servlet 容器:

<%-- PHP 頁面中 --%>
$date = new Java("java.util.Date");
echo $date->toString();

// 訪問 Session
$session = $request->getSession();
$attrs = $session->getAttributeNames();

雖生產環境較少見,但展示了「Java 作為底層平台,多語言共存」的架構想像力。

避坑指南與最佳實踐

  • 安全性:腳本具備完整 Java 權限,嚴禁執行不可信用戶腳本,或使用 SecurityManager 沙箱。
  • 除錯困難:IDE 對 JS 斷點支援有限,建議將核心邏輯保留在 Java,僅外部化高層規則。
  • 執行緒安全:ScriptEngine 非線程安全,多線程環境下每線程建立實例或使用 ThreadLocal 池化。
  • 編碼問題:讀取腳本檔案時顯式指定 new InputStreamReader(is, "UTF-8")。

總結

JSR-223 + Rhino 為 Java 應用注入了動態性,適合業務規則引擎、用戶腳本擴展、測試腳本等場景。掌握 eval/Bindings/Compilable/Invocable 四大核心模式,即可駕馭腳本與 Java 的雙向互操作。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved