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

Java SE 6 新特性:腳本語言整合 (JSR 223) 完整指南

     編輯:Java 相關知識

Java 平台與腳本語言無縫融合:javax.script API 深度解析

Java SE 6 (Mustang) 引入 JSR 223: Scripting for the Java Platform,定義統一的 javax.script API,讓 Java 應用能以標準方式調用 JavaScript、Groovy、Ruby、Python 等 25+ 種腳本引擎。本文系統講解腳本引擎發現機制、執行上下文、資料交換、函數調用、腳本編譯緩存等核心功能,附完整代碼範例。

架構總覽

┌─────────────────────────────────────────────────────────────┐
│                    Java Application                          │
│  ┌───────────────────────────────────────────────────────┐  │
│  │              ScriptEngineManager                        │  │  ← 統一入口:發現/建立引擎
│  │  • getEngineByName("JavaScript")                        │  │
│  │  • getEngineFactories()                                 │  │
│  └─────────────────────┬──────────────────────────────────┘  │
│                        │                                      │
│  ┌─────────────────────▼──────────────────────────────────┐  │
│  │                   ScriptEngine (介面)                    │  │  ← 抽象腳本引擎
│  │  • eval(String/Reader)                                  │  │
│  │  • getContext() / setContext()                          │  │
│  │  • getBindings() / setBindings()                        │  │
│  └─────────────────────┬──────────────────────────────────┘  │
│                        │                                      │
│  ┌─────────────────────▼──────────────────────────────────┐  │
│  │              ScriptContext / Bindings                    │  │  ← 執行上下文與資料交換區
│  │  • ENGINE_SCOPE (引擎級)  GLOBAL_SCOPE (全域)             │  │
│  │  • put/get/remove 屬性                                   │  │
│  └──────────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────┘
                        │
         ┌──────────────┼──────────────┐
         ▼              ▼              ▼
    JavaScript      Groovy          JRuby / Jython / ...
    (內建)          (需 Jar)        (需 Jar)

核心 API 快速入門

Hello World (JavaScript)


import javax.script.*;

public class HelloWorld {
    public static void main(String[] args) throws ScriptException {
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("JavaScript");
        engine.eval("print('Hello World')");  // 輸出: Hello World
    }
}

從檔案執行腳本


public class RunScript {
    public static void main(String[] args) throws Exception {
        String engineName = args[0];  // "javascript", "groovy", ...
        String scriptFile = args[1];  // "script.js", "script.groovy"

        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName(engineName);
        engine.eval(new FileReader(new File(scriptFile)));
    }
}
// 執行: java RunScript javascript script.js

腳本引擎發現機制 (Service Provider)

  1. ScriptEngineManager 掃描 Classpath 所有 JAR
  2. 尋找 META-INF/services/javax.script.ScriptEngineFactory 檔案
  3. 檔案內容為實作類全名 (如 com.sun.script.javascript.RhinoScriptEngineFactory)
  4. 實例化 Factory → factory.getScriptEngine() 產生引擎

優勢:零代碼切換/升級腳本引擎,只需替換 JAR。
腳本引擎發現時序圖

執行上下文與資料交換 (ScriptContext / Bindings)

三層作用域 (優先級遞增):

層級來源可見範圍典型用途
GLOBAL_SCOPEScriptEngineManager同一 Manager 建立的所有引擎全域配置、共用服務物件
ENGINE_SCOPEScriptEngine 實例單一引擎實例內的所有腳本引擎級共享變數、函數庫
局部 (eval 參數)eval(script, bindings/context)單次 eval 調用臨時參數、覆蓋測試

作用域優先級演示


public class ScopeTest {
    public static void main(String[] args) throws Exception {
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("javascript");
        String script = "println(greeting)";

        // 1. 全域層
        manager.put("greeting", "Hello from ScriptEngineManager");
        engine.eval(script);  // 輸出: Hello from ScriptEngineManager

        // 2. 引擎層 (覆蓋全域)
        engine.put("greeting", "Hello from ScriptEngine");
        engine.eval(script);  // 輸出: Hello from ScriptEngine

        // 3. 局部層 (最高優先級)
        ScriptContext ctx = new SimpleScriptContext();
        ctx.setAttribute("greeting", "Hello from eval method", ScriptContext.ENGINE_SCOPE);
        engine.eval(script, ctx);  // 輸出: Hello from eval method
    }
}

ScopeTest 輸出結果

Bindings 分佈圖

進階:放入 Java 物件供腳本調用方法


engine.put("myList", new ArrayList());  // 放入 Java 集合
engine.eval("myList.add('from JS'); println(myList.size())");  // 腳本直接調用 Java 方法

I/O 重導向


PipedReader pr = new PipedReader();
PipedWriter pw = new PipedWriter(pr);
engine.getContext().setWriter(new PrintWriter(pw));
engine.eval("println('captured')");
new BufferedReader(pr).readLine();  // 讀取腳本輸出

可選介面:Invocable (函數調用) 與 Compilable (編譯緩存)

Invocable:調用腳本函數/方法


String script = "function greeting(msg) { println(msg); }";
engine.eval(script);
if (engine instanceof Invocable) {
    Invocable inv = (Invocable) engine;
    inv.invokeFunction("greeting", "hi from Java");  // 直接調用腳本函數
    // inv.invokeMethod(obj, "methodName", args);  // 調用腳本物件方法
}

注意:調用不存在函數拋出 NoSuchMethodException,需捕獲。

Compilable:腳本預編譯緩存


String script = "println(greeting); greeting = 'Good Afternoon!'";  // 有副作用
engine.put("greeting", "Good Morning!");
if (engine instanceof Compilable) {
    Compilable comp = (Compilable) engine;
    CompiledScript cs = comp.compile(script);  // 編譯為中間碼/位元碼
    cs.eval();  // 第一次: Good Morning!
    cs.eval();  // 第二次: Good Afternoon! (狀態被修改)
}

CompilableTest 輸出

關鍵特性:CompiledScript 可重複執行避免重複解析,但**狀態變更會保留**,適用於無副作用或刻意保留狀態的場景。

jrunscript 命令列工具

  • 互動模式:jrunscript → 直接輸入腳本行,即時執行 (REPL)
  • 批次模式:jrunscript script.js 或 jrunscript -l groovy script.groovy
  • 跨平台萬能腳本執行器,**實驗性功能**,未來版本可能移除

支援腳本語言生態 (需額外 JAR)

官方彙整頁面提供 25+ 語言 JSR 223 適配器:Groovy、JRuby、Jython、Scala、Clojure、BeanShell、Rhino (JS)、Lua、Scheme、Tcl 等。
使用方式:下載對應 JAR 放入 Classpath,getEngineByName("groovy") 即可使用,Java 代碼零修改。

最佳實踐建議

  1. 隔離腳本權限:生產環境使用 SecurityManager 限制腳本存取檔案/網路/反射
  2. 快取 CompiledScript:熱點腳本預編譯緩存,顯著降低 CPU
  3. 明確作用域:避免濫用 GLOBAL_SCOPE,優先用 ENGINE_SCOPE 或局部 Bindings
  4. 錯誤處理:捕獲 ScriptException (語法錯誤)、NoSuchMethodException (函數不存在)
  5. 引擎選型:
    • Rhino (JS):內建、零依賴、效能佳
    • Groovy:語法最接近 Java、無縫互操作
    • JRuby/Jython:現有 Ruby/Python 資產復用

JSR 223 讓 Java 平台成為多語言執行環境。善用腳本語言處理「易變業務規則、模板渲染、動態配置、DSL 表達」,能大幅提升開發彈性與交付速度。

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