腳本、編譯與註解處理
腳本 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);
重定向輸入和輸出
可以通過調用腳本上下文的 setReader
和 setWriter
方法來重定向腳本的標準輸入和輸出
StringWriter writer = new StringWriter();
engine.getContext().setWriter(new PrintWriter(writer, true));
任何 JavaScript 的 print
和 println
函數產生的輸出都會被髮送到 writer
。 setReader
和 setWriter
方法只會影響腳本引擎的標準輸入和輸出源
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.out
和 System.err
。run
方法的第一個參數是輸入流,由於編譯器不會接受任何控制檯輸入,因此總是應該讓其保持爲 null
。( run
方法是 Tool
接口繼承而來的,它考慮到某些工具需要讀取輸入)
如果在命令行調用 javac
,那麼 run
方法其餘的參數就會作爲變量傳遞給 javac
。這些變量是一些選項或文件名
發起編譯任務
可以通過使用 CompilationTask
對象來對編譯過程進行更多的控制。特別是,你可以:
- 控制程序代碼的來源,例如,在字符串構建器而不是文件中提供代碼
- 控制類文件的放置位置,例如,存儲在數據庫中
- 監聽在編譯過程中產生的錯誤和警告信息
- 在後臺運行編譯器
捕獲診斷信息
javax.tools.DiagnosticListener
從內存中讀取源文件
略
將字節碼寫出到內存中
略