java平台脚本+java编译器API
生活随笔
收集整理的這篇文章主要介紹了
java平台脚本+java编译器API
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
【0】README
0.1)本文文字描述轉自 core java volume 2, 旨在學習 ?java平臺腳本+java編譯器API 的 基礎知識;
------------------------------------------------------------------------------
【1】java平臺的腳本?
1)腳本語言定義:?腳本語言是一種通過在運行時解釋程序文本,從而避免使用通常的編輯/編譯/鏈接/運行循環的語言。(干貨——腳本語言定義,如JavaScript) 2)腳本語言有很多優勢(merit): m1)便于快速變更,鼓勵不斷試驗; m2)可以修改運行著的程序的行為; m3)支持程序用戶的定制化; 3)problem+solution: 3.1)problem:大多數腳本語言都缺乏可以使編寫復雜應用受益的特性,如,強類型,封裝和模塊化; 3.2)solution:因此人們在嘗試將腳本語言和傳統語言的優勢相結合。。腳本API 使你可以在 java 平臺上實現這個目的, 他支持在java程序中使用 JavaScript, Groovy, Ruby, 甚至更奇異的注入 Scheme 和 Haskell 等語言編寫的腳本進行調用;(干貨——java腳本API的作用)
【1.1】獲取腳本引擎 1)腳本引擎:?腳本引擎是一個可以執行用某種特定語言編寫的腳本的類庫。當虛擬機啟動時, 他會發現這些引擎;(干貨——腳本引擎定義) 2)為了枚舉這些引擎:需要構造一個 ScriptEngineManager , 并調用 getEngineFactories 方法。 可以向每個引擎工廠去詢問他們所支持的引擎名, MIME類型和文件擴展名;如下表所示:
3)獲取引擎:可以直接通過名字, MIME 類型 或文件擴展名來請求它: <span style="font-family:Microsoft YaHei;font-size:14px;">ScriptEngine engine = manager.getEngineByName("JavaScript");</span>
【1.2】 腳本賦值與綁定 1)利用引擎調用腳本:?Object result = engine.eval(script); 2)如果腳本存儲在文件中, 那么需要打開一個 Reader, 然后調用:?Object result = engine.eval(reader); 3)可以在同一個引擎上調用多個腳本:? <span style="font-family:Microsoft YaHei;font-size:14px;">engine.eval("n=1728"); Object result = engine.eval("n+1") 返回 1729;</span> Attention) 要想知道在多個線程中并發執行腳本是否安全, 可以調用: Object param = factory.getParameter("THREADING");?返回值有:? A1)null:(不安全); A2)MULTITHREADED:?并發執行安全;? A3)THREAD-ISOLATED:除了MULTITHREADED外,會為每個線程維護不同的變量綁定; A4)STATELESS:?除了THREAD-ISOLATED外, 腳本不會改變變量綁定; 4) 向引擎中添加變量綁定: <span style="font-family:Microsoft YaHei;font-size:14px;">engine.put(k, 100); Object result = engine.eval("k+1");</span> 5)獲取由腳本語句綁定的變量: <span style="font-family:Microsoft YaHei;font-size:14px;">engine.eval("n=100"); Object result = engine.get("n");</span> 6)除了向引擎或全局作用域添加綁定外,還可以將綁定收集到一個類型為 Bindings 的對象中, 然后將其傳遞給 eval 方法: <span style="font-family:Microsoft YaHei;font-size:14px;">Bindings scope = engine.createBindings(); scope.put(b, new Butten()); engine.eval(script, scope);</span>
<span style="font-family:Microsoft YaHei;font-size:14px;">package com.corejava.chapter10;import java.io.File; import java.io.FileNotFoundException; import java.io.PrintWriter; import java.io.StringWriter; import java.util.List;import javax.script.Bindings; import javax.script.ScriptEngine; import javax.script.ScriptEngineFactory; import javax.script.ScriptEngineManager; import javax.script.ScriptException;public class ScriptTest {public static void main(String[] args) throws ScriptException, FileNotFoundException{// 腳本引擎管理器//ScriptEngineManager manager = new ScriptEngineManager();/*List<ScriptEngineFactory> list = manager.getEngineFactories();for(ScriptEngineFactory engine : list){System.out.println(engine.getEngineName());}*/// 腳本引擎管理器ScriptEngineManager manager = new ScriptEngineManager();ScriptEngine engine = manager.getEngineByName("JavaScript");// 向引擎中添加變量綁定engine.eval("n=1728");Object result = engine.eval("n+1"); // 1729.0System.out.println(result);// 獲取由腳本語句綁定的變量 engine.put("k", 100);result = engine.eval("k+1"); // 101System.out.println(result);// 除了向引擎或全局作用域添加綁定外,還可以將綁定收集到一個類型為 Bindings 的對象中, // 然后將其傳遞給 eval 方法:Bindings scope = engine.createBindings();scope.put("a", "hello, ");result = engine.eval("a+ 'world!'", scope); // hello, world!System.out.println(result);// 重定向輸入和輸出// 任何用 js 的 print 和 println函數產生的輸出都會發送到 writerStringWriter writer = new StringWriter();engine.getContext().setWriter(new PrintWriter(new File("com/corejava/chapter10/output.txt")));engine.eval("print('hello world, this msg is from java app')");} }</span> Attention)你可能希望除了引擎作用域和全局作用域外還有其他的作用域。但這需要你自己去解決。你需要實現一個類, 他實現了 ScriptContext 接口, 并管理著一個作用域集合。每個作用域都是由一個整數標識的, 而且越小的數字應該越先被搜索;
【1.3】重定向輸入和輸出 1)重定向輸入和輸出的實現:通過調用腳本上下文的 setReader 和 setWriter 方法來實現; <span style="font-family:Microsoft YaHei;font-size:14px;">StringWriter writer = new StringWriter(); engine.getContext().setWriter(new PrintWriter(writer, true));</span> 對以上代碼的分析(Analysis):??在上例中, 任何用 js 的 print 和 println函數產生的輸出都會發送到 writer; Warning)可以向setWriter 方法傳遞任何Writer, 但是如果傳遞的不是 PrintWriter, Rhino 引擎就會拋出異常;
<span style="font-family:Microsoft YaHei;font-size:14px;">// 重定向輸入和輸出(同上)// 任何用 js 的 print 和 println函數產生的輸出都會發送到 writerStringWriter writer = new StringWriter();engine.getContext().setWriter(new PrintWriter(new File("com/corejava/chapter10/output.txt")));engine.eval("print('hello world, this msg is from java app')");</span>
【1.4】調用腳本的函數和方法 1)提供調用腳本的函數和方法的 腳本引擎實現了 Invocable 接口; 2)如何調用一個函數??需要用函數名來調用 invokeFunction 方法,函數名后面是函數的參數: <span style="font-family:Microsoft YaHei;font-size:14px;">if(engine implemens Invocable)(Invocable)engine.invockFunction("aFunction", param1, param2);</span> 3) 如果腳本是面向對象的, 像下面這樣調用方法: <span style="font-family:Microsoft YaHei;font-size:14px;"> (Invocable)engine.invockMethod(implicitParam, "aMethod", explicitParam1, explicitParam2);</span> 3.1)這里, implicitParam對象是用腳本語言編寫的對象的一個代理, 他必須是前一個腳本引擎調用的結果; 4)讓腳本引擎去實現一個 java 接口, 然后就就可以用 java 方法調用的語法來調用該腳本函數; 4.1)看個荔枝: step1)java接口: <span style="font-family:Microsoft YaHei;font-size:14px;">public interface Greeter {String greet(String whom); }</span> step2)在腳本引擎如Rhino中, 可以提供下面的函數: <span style="font-family:Microsoft YaHei;font-size:14px;">function greet(x) {return "hello" + x + "!"; }</span> step3)以上代碼必須先計算,然后調用: <span style="font-family:Microsoft YaHei;font-size:14px;">Greeter g = ((Invocable)engine).getInterface(Greeter.class);</span> step4)?產生一個普通的java 方法調用: <span style="font-family:Microsoft YaHei;font-size:14px;">String result = g.greet("world");</span>
<span style="font-family:Microsoft YaHei;font-size:14px;">public class InvocableTest {public static void main(String[] args) throws FileNotFoundException, ScriptException{// 腳本引擎管理器ScriptEngineManager manager = new ScriptEngineManager();ScriptEngine engine = manager.getEngineByName("JavaScript");engine.eval(new FileReader("com/corejava/chapter10/test.js"));Greeter g = ((Invocable)engine).getInterface(Greeter.class);String result = g.greet("World");System.out.println(result);// hello, World} }</span> 5)在面向對象的腳本語言中, 可以通過相匹配的 java 接口來訪問一個腳本類。 5.1)看個荔枝:? step1)下面的代碼定義了一個 SimpleGreeter類, <span style="font-family:Microsoft YaHei;font-size:14px;">function SimpleGreeter(salutation) {this.salutation = salutation}; SimpleGreeter.prototype.greet = function(whom){return this.salutation + ", " + whom + "!" ;}</span> step2)?在對 JavaScript的類定義計算后, 可以調用: <span style="font-family:Microsoft YaHei;font-size:14px;">Object greeter = engine.eval("new SimpleGreenter('goodbye')"); Greeter g = ((Invocable)engine).getInterface(greeter , Greeter.class);</span> step3)?當調用 g.greet("world")時, greet 方法會在 js 對象 greenter 上被調用。 其結果是 “goodbye”;
<span style="font-family:Microsoft YaHei;font-size:14px;">public class SimpleGreeter {public static void main(String[] args) throws FileNotFoundException, ScriptException{// 腳本引擎管理器ScriptEngineManager manager = new ScriptEngineManager();ScriptEngine engine = manager.getEngineByName("JavaScript");engine.eval(new FileReader("com/corejava/chapter10/test.js"));Object goodbyeGreeter = engine.eval("new SimpleGreeter('goodbye')");Greeter g = ((Invocable)engine).getInterface(goodbyeGreeter, Greeter.class);String result = g.greet("World");System.out.println(result);// goodbye, World} }</span><span style="font-family:Microsoft YaHei;font-size:14px;">function greet(x) {return "hello, " + x + "!"; }function SimpleGreeter(salutation) {this.salutation = salutation; } SimpleGreeter.prototype.greet = function(whom) {return this.salutation + ", " + whom + "!" ; }</span> Conclusion)?如果你希望從 java 中調用腳本代碼, 同時又不想因這種腳本語言的語法而受到困擾, 那么Invocable 接口就很有用;
【1.5】 編譯腳本 Attention)?將腳本進行編譯后,再執行; 1)應用背景:某些腳本引擎處于對執行效率的考慮,可以將腳本代碼編譯為某種中間格式。這些引擎實現了 Compilable 接口; 2)看個荔枝: <span style="font-family:Microsoft YaHei;font-size:14px;">Reader reader = new FileReader("script.js"); CompiledScript script = null; if(engine implements Compilable)script = ((Compilable )engine).compile(reader);</span> 3)一旦腳本被編譯,就可以執行它,如果引擎不支持編譯, 則執行原始腳本, 如: <span style="font-family:Microsoft YaHei;font-size:14px;">if(script != null)script.eval(); elseengine.eval(reader);</span> Attention)?當然,只有需要重復執行代碼時, 我們才希望編譯腳本;(干貨——為什么編譯腳本?——只有需要重復執行代碼時, 我們才希望編譯腳本)
<span style="font-family:Microsoft YaHei;font-size:14px;">public class CompileTest {public static void main(String[] args) throws FileNotFoundException, ScriptException{// 腳本引擎管理器ScriptEngineManager manager = new ScriptEngineManager();ScriptEngine engine = manager.getEngineByName("JavaScript");// 讀取器Reader reader = new FileReader("com/corejava/chapter10/test.js");CompiledScript script = null;if(engine instanceof Compilable){script = ((Compilable)engine).compile(reader); // 編譯腳本}if(script != null){script.eval();System.out.println("執行編譯后的腳本");}else{engine.eval(reader);System.out.println("執行腳本源碼");}Object goodbyeGreeter = engine.eval("new SimpleGreeter('goodbye')");Greeter g = ((Invocable)engine).getInterface(goodbyeGreeter, Greeter.class);String result = g.greet("World");System.out.println(result);// goodbye, World} }</span>Attention) for complete source code about instances ?above, please visit?https://github.com/pacosonTang/core-java-volume/tree/master/coreJavaAdvanced/chapter10/10_1??
【2】編譯器API 1)有許多工具都需要調用java 編譯器,如(tools): t1)?開發環境; t2)?java 教學和輔導程序; t3)自動化的構建和測試工具; t4)處理java 代碼段的模板工具, 如 java server page(JSP); 2)?在過去, 應用程序是通過在 jdk/lib/tools.jar類庫中未歸檔的類調用 java 編譯器的。 3)從java SE6 開始, 用于編譯的一個公共API 成為 java 平臺的一部分,并且它再也不需要使用 tools.jar 了;
【2.1】編譯便捷之法 1)調用編譯器的荔枝: <span style="font-family:Microsoft YaHei;font-size:14px;">JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); OutputStream out = ... OutputStream err = ... int result = compiler.run(null, out, err, "-sourcepath", "src", "Test.java"); // 返回值為0 表示編譯成功;</span>
package com.corejava.chapter10_2;import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.OutputStream;import javax.tools.JavaCompiler; import javax.tools.ToolProvider;public class JavaCompilerTest {public static void main(String[] args) throws FileNotFoundException{JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();OutputStream out = new FileOutputStream("out.txt");OutputStream err = new FileOutputStream("err.txt");System.out.println(compiler);System.out.println(System.getProperty("java.home"));int result = compiler.run(null, out, err, "com/corejava/chapter10_2/Hello.java");if(result == 0){System.out.println("bingo");}else{System.out.println("oops");}} } 對以上代碼的分析(Analysis): A1)?編譯器會向 提供給他 的流發送輸出和錯誤消息; A2)如果將這些參數設置為null, 就會使用 System.out 和 System.err; A3)?run 方法的第一個參數是輸入流, 由于編譯器不會接受任何控制臺輸入, 因此總是應該讓其保持為null; A4)?如果在命令行調用 javac, 那么run 方法其余的參數就會作為 變量傳遞給 javac;
【2.2】使用編譯工具(for detailed info, please visit http://blog.csdn.net/pacosonswjtu/article/details/50718602) 1)可以通過使用 CompilationTask 對象來對編譯過程進行更多的控制, 如(controls): c1)控制程序代碼的來源; c2)控制類文件的位置; c3)監聽在編譯過程中產生的錯誤和警告消息; c4)在后臺運行編譯器; 2)源代碼和類文件的位置:由 JavaFileManager 控制的, 它負責確定源代碼和類文件的 JavaFileObject 實例; 3) 為了監聽錯誤消息, 需要安裝一個 DiagnosticListener:?這個監聽器在編譯器報告警告或錯誤消息時會收到一個 Diagnostic 對象。 DiagnosticCollector類實現了這個接口, 他將收集所有的 診斷信息, 使得你可以在編譯完成之后遍歷這些信息; 4)通過調用JavaCompiler 類的 getTaksk 方法 來獲得 CompilationTask 對象, 這時需要指定(sepecification): s1)一個用于所有編譯器輸出的Writer:?如果是null, 則使用 System.err; s2)一個 JavaFileManager: ?如果是null, 則使用編譯器的標準文件管理器; s3)一個 DiagnosticListener; s4)選項字符串:??如果沒有選項, 則為null; s5)用于注解處理的類名字:?如果沒有指定類名字, 則為null; s6)用于源文件的 JavaFileObject 實例; 5)需要為最后3個參數提供 Iterable 對象, 如: Iterable<String> options = Arrays.asList("-g", "-d", "classes"); 6)如果希望編譯器從磁盤讀取源文件, 可以讓 StandardJavaFileManager 將文件 名字符串或 File 對象轉譯成 JavaObject 實例, 如:<span style="font-family:Microsoft YaHei;font-size:14px;">StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null) ; Iterable<JavaFileObject> fileObjects = fileManager.getJavaFileObjectsFromStrings(filename);</span> 7) 如果希望 編譯器從磁盤之外的地方讀取 源代碼:?可以提供自己的 JavaFileObject 的子類; 8)CompilationTask接口:?擴展了Callable<Boolean>接口,可以將其傳遞給一個 Executor, 使其可以在另一個線程中執行, 或者可以直接調用call 方法。返回值如果是 false, 則調用失敗; <span style="font-family:Microsoft YaHei;font-size:14px;">Callable<Boolean> task = new JavaCompiler.compilationTasks(null, fileManager, diagnostics, options, null, fileObjects); if(!task.call())println("compilation failed");</span> 9)problem+solution: 9.1)problem:事實證明,要告知編譯器的文件管理器去使用這些文件對象還是比較棘手的,因為類庫沒有提供實現了 StandardJavaFileManager 接口的類; 9.2)solution:我們需要子類化 ForwardingJavaFileManager 類, 該類將所有的調用都代理給了給定的文件管理器。 Conclusion) C1)?總之,如果只是想以常規的方式調用編譯器, 那就只需要調用 JavaCompiler 任務的run方法, 去讀寫磁盤文件; C2)如果想對文件處理和錯誤報告進行更多 的控制, 可以使用 CompilationTask 類;
? 創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎
總結
以上是生活随笔為你收集整理的java平台脚本+java编译器API的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 无价之宝的之是什么意思 无价之宝的之的含
- 下一篇: java编译器API——使用编译工具