Java 6 引入 JSR-223 (javax.script) 標準化腳本引擎 API,使 Java 應用能夠動態加載、編譯、執行 JavaScript、Groovy 等腳本語言。這對於規則引擎、動態配置、用戶自定義邏輯擴展等場景極具價值。
ScriptEngineManager:引擎管理器,負責發現和創建引擎實例。ScriptEngine:腳本引擎接口,提供 eval() 執行腳本。Invocable:可調用接口,允許調用腳本中定義的具體函數/方法。Bindings / ScriptContext:作用域綁定,用於在 Java 與腳本間共享變量/對象。以下代碼演示:將 Java 對象 (JFrame, List) 暴露給 JS,執行 JS 文件,調用 JS 函數,獲取返回值,並反向調用 Java 方法。
import javax.script.*;
import javax.swing.JFrame;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.List;
public class ScriptDemo {
public static void main(String[] args) throws Exception {
// 1. 獲取 JS 引擎(默認 Rhino/Nashorn)
ScriptEngine engine = new ScriptEngineManager().getEngineByExtension("js");
System.out.println("是否支持 Invocable: " + (engine instanceof Invocable));
// 2. 準備共享對象
JFrame frame = new JFrame("Script Demo");
List<String> list = new ArrayList<>();
// 3. 綁定到引擎作用域
Bindings bindings = engine.createBindings();
bindings.put("jf", frame);
bindings.put("list", list);
engine.setBindings(bindings, ScriptContext.ENGINE_SCOPE);
// 4. 執行外部 JS 文件
Object result = engine.eval(new FileReader("script.js"));
System.out.println("腳本執行返回值: " + result); // 通常為 null
// 5. 調用腳本中定義的函數
if (engine instanceof Invocable) {
Invocable invocable = (Invocable) engine;
// 調用無參函數
invocable.invokeFunction("testJS"); // 輸出: Hello world!
// 調用有返回值函數
List<String> names = (List<String>) invocable.invokeFunction("getNames");
System.out.println("JS 返回列表: " + names); // [doSth, getNames]
// 調用操作 Java 對象的函數
invocable.invokeFunction("doSth"); // 顯示 JFrame 窗口
}
}
}
function doSth() {
jf.setSize(500, 300);
jf.setVisible(true);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
function getNames() {
list.add("doSth");
list.add("getNames");
return list;
}
function testJS() {
print('Hello from JavaScript!');
}
ENGINE_SCOPE 使綁定對象在整個引擎生命周期內可見。還有 GLOBAL_SCOPE 跨引擎共享。
JS 可直接調用 Java 對象的公共方法、訪問屬性(如 jf.setSize())、甚至實現 Java 接口。集合操作 (list.add()) 亦無縫互通。
eval() 和 invokeFunction() 拋出 ScriptException、NoSuchMethodException,生產環境務必捕獲處理。
CompiledScript 預編譯。invokedynamic。Java 腳本引擎 API 為 JVM 語言互操作提供了標準橋樑。通過 Invocable 和 Bindings,實現了 Java 與 JavaScript 的深度雙向交互。合理使用可大幅提升系統靈活性,但需注意安全性(沙箱)、性能(複用/編譯)與維護性(腳本管理)平衡。