20230706 8. 腳本、編譯與註解處理

腳本、編譯與註解處理

腳本 API 使你可以調用諸如 JavaScript 和 Groovy 這樣的腳本語言代碼;當你希望在應用程序內部編譯 Java 代碼時,可以使用編譯器 API ;註解處理器可以在包含註解的 Java 源代碼和類文件上進行操作。如你所見,有許多應用程序都可以用來處理註解,從簡單的診斷到“字節碼工程”,後者可以將字節碼插入到類文件中,甚至可以插入到運行程序中

Java 平臺的腳本

腳本語言是一種通過在運行時解釋程序文本,從而避免使用通常的編輯 編譯/鏈接 /運行循環的語言。腳本語言有許多優勢

  • 便於快速變更,鼓勵不斷試驗
  • 可以修改運行着的程序的行爲
  • 支持程序用戶的定製化

另一方面,大多數腳本語言都缺乏可以使編寫複雜應用受益的特性,例如強類型、封裝和模塊化

人們在嘗試將腳本語言和傳統語言的優勢相結合。腳本 API 使你可以在 Java 平臺上實現這個目的,它支持在 Java 程序中對用 JavaScript 、Groovy 、Ruby ,甚至是更奇異的諸如 Scheme 和 Haskell 等語言編寫的腳本進行調用

獲取腳本引擎

腳本引擎是一個可以執行用某種特定語言編寫的腳本的類庫。當虛擬機啓動時,它會發現可用的腳本引擎。爲了枚舉這些引擎,需要構造一個 ScriptEngineManager ,並調用 getEngineFactories 方法。可以向每個引擎工廠詢問它們所支持的引擎名、MIME 類型和文件擴展名

腳本引擎工廠的屬性:

引擎 名字 MIME 類型 文件擴展
Rhino (Javascript) rhino, Rhino, JavaScript, javascript application/javascript, application/ecmascript, text/javascript, text/ecmascript js
Groovy groovy groovy
Renjin Renjin text/x-R R, r, S, s

通常,你知道所需要的引擎,因此可以直接通過名字 MIME 類型或文件擴展來請求它。Oracle JDK 以往都包含一個 JavaScript 引擎,但是在 Java 15 中被移除了

Rhino 沒有遵守 Java 標準,而是使用自己的一套 API

引入 Nashorn

<dependency>
    <groupId>org.openjdk.nashorn</groupId>
    <artifactId>nashorn-core</artifactId>
    <version>15.4</version>
</dependency>
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("javascript");
Object eval = engine.eval("1+8");
System.out.println(eval);       // 9

可以通過在類路徑中提供必要的 JAR 文件來添加對更多語言的支持

ScriptEngineManager manager = new ScriptEngineManager();
List<ScriptEngineFactory> engineFactories = manager.getEngineFactories();
for (ScriptEngineFactory engineFactory : engineFactories) {
    System.out.println(engineFactory.getEngineName());
    System.out.println(engineFactory.getEngineVersion());
    System.out.println(engineFactory.getNames());
}
/*
    Oracle Nashorn
    1.8.0_302
    [nashorn, Nashorn, js, JS, JavaScript, javascript, ECMAScript, ecmascript]
 */
javax.script.ScriptEngineManager 方法名稱 方法聲明 描述
getEngineFactories public List<ScriptEngineFactory> getEngineFactories() 獲取所有發現的引擎工廠的列表
getEngineByName
getEngineByExtension
getEngineByMimeType
public ScriptEngine getEngineByName(String shortName)
public ScriptEngine getEngineByExtension(String extension)
public ScriptEngine getEngineByMimeType(String mimeType)
獲取給定名字、腳本文件擴展名或陽ME 類型的腳本引擎
javax.script.ScriptEngineFactory 方法名稱 方法聲明 描述
getNames
getExtensions
getMimeTypes
public List<String> getNames();
public List<String> getExtensions();
public List<String> getMimeTypes();
獲取該工廠所瞭解的名字、腳本文件擴展名和 MIME 類型

腳本賦值與綁定

一旦擁有了引擎,就可以通過下面的調用來直接調用腳本:

Object result = engine.eval(scriptString);

重定向輸入和輸出

可以通過調用腳本上下文的 setReadersetWriter 方法來重定向腳本的標準輸入和輸出

StringWriter writer = new StringWriter();
engine.getContext().setWriter(new PrintWriter(writer, true));

任何 JavaScript 的 printprintln 函數產生的輸出都會被髮送到 writersetReadersetWriter 方法只會影響腳本引擎的標準輸入和輸出源

Nashorn 引擎沒有標準輸入源的概念,因此調用 setReader 沒有任何效果

調用腳本的函數和方法

在使用許多腳本引擎時,都可以調用腳本語言的函數,而不必對實際的腳本代碼進行計算。

提供這種功能的腳本引擎實現了 Invocable 接口。特別是, Nashorn 引擎就是實現了 Invocable 接口

編譯腳本

某些腳本引擎出於對執行效率的考慮,可以將腳本代碼編譯爲某種中間格式。這些引擎實現了 Compilable 接口

當然,只有需要重複執行時,我們才希望編譯腳本

編譯器 API

調用編譯器

JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
OutputStream outStream = null;
OutputStream errrStream = null;
int result = compiler.run(null, outStream, errrStream, "-sourcepath", "src", "Test.java");
System.out.println(result);

返回值爲 0 表示編譯成功

編譯器會向提供給它的流發送輸出和錯誤消息。如果將這些參數設置爲 null ,就會使用 System.outSystem.errrun 方法的第一個參數是輸入流,由於編譯器不會接受任何控制檯輸入,因此總是應該讓其保持爲 null 。( run 方法是 Tool 接口繼承而來的,它考慮到某些工具需要讀取輸入)

如果在命令行調用 javac ,那麼 run 方法其餘的參數就會作爲變量傳遞給 javac 。這些變量是一些選項或文件名

發起編譯任務

可以通過使用 CompilationTask 對象來對編譯過程進行更多的控制。特別是,你可以:

  • 控制程序代碼的來源,例如,在字符串構建器而不是文件中提供代碼
  • 控制類文件的放置位置,例如,存儲在數據庫中
  • 監聽在編譯過程中產生的錯誤和警告信息
  • 在後臺運行編譯器

捕獲診斷信息

javax.tools.DiagnosticListener

從內存中讀取源文件

將字節碼寫出到內存中

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章