摘自:https://www.cnblogs.com/youruike-/p/12053564.html
Java編譯器API簡介
今天給大家分享的是Java編譯器API簡介,文章部分內容摘自【優銳課】學習筆記。
Java編譯器API
Java編譯器API是Java模塊(稱為java.compiler)的一部分。該模塊包括語言模型和注釋處理,以及編譯器API。它定義了Java編程語言和編譯器工具的類型和模型聲明,可以在執行期間從應用程序代碼中調用它們。注釋處理有助於訪問注釋處理器,可以將其視為Java編譯器的插件。它使注釋處理器和注釋處理工具環境之間能夠通信。模型,元素和類型包處理Java編程語言的元素,而util包則幫助處理程序元素和類型。
編譯工具
javax.tools包提供了與Java編譯器一起使用的接口和類,並且可以在執行期間從程序中調用它。 它提供了一個框架,該框架允許客戶端從其自己的應用程序代碼定位和運行編譯器。它還提供了服務提供者接口(SPI),用於對診斷的結構化訪問和用於覆蓋文件訪問的文件抽象。ToolProvider類提供了編譯器API的入口點。此類提供了一些方法來定位編譯器的工具提供者。 例如,我們可以輕松地找到系統中安裝的編譯器支持的Java源版本列表。
1 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); 2 for(SourceVersion sv:compiler.getSourceVersions()){ 3 System.out.println(sv); 4 }
輸出如下(根據系統中安裝的版本)。
1 RELEASE_3 2 RELEASE_4 3 RELEASE_5 4 RELEASE_6 5 RELEASE_7 6 RELEASE_8 7 RELEASE_9 8 RELEASE_10 9 RELEASE_11
在這種情況下,ToolProvider會找到默認的編譯器。通過使用服務提供者機制,還可以找到替代的編譯器或工具。如果某些供應商提供Java編譯器,則jar文件將包含文件META-INF / service / javax.tool.JavaCompiler,並且將包含一行:com.vendor.VendorJavaCompiler。我們可以將jar文件放入類路徑中,並按以下方式定位它:
1 JavaCompiler vendorJavaCompiler = 2 ServiceLoader.load(JavaCompiler.class).iterator().next();
ServiceProvider是Java的util類之一,用於查找和加載部署在執行環境中的服務提供者。
找到JavaCompiler后,就可以通過Java源代碼執行各種編譯診斷任務。為了說明這個想法,讓我們首先創建一個簡單的類,如下所示:
1 package com.mano.jcapidemo; 2 import java.util.Random; 3 public class MyClass { 4 public static void main(String[] args){ 5 Random r = new Random(); 6 System.out.println("Today your Lucky Number is: 7 "+r.nextInt(10)); 8 } 9 }
現在,在創建Java源文件之后,我們可以使用名為DiagnosticCollector的診斷收集器類將診斷收集在列表中。
創建另一個類,從該類中我們將調用編譯器來編譯上述類MyClass,並將診斷信息報告給該類。換句話說,我們將創建一個應用程序來加載Java源文件,並由Java編譯器對其進行編譯,並且,如果源代碼中有任何錯誤,請確保將其報告給主機應用程序。
1 package com.mano.jcapidemo; 2 3 import com.mano.annotation.CustomAnnotation; 4 import java.util.Set; 5 import javax.annotation.processing.AbstractProcessor; 6 import javax.annotation.processing.RoundEnvironment; 7 import javax.annotation.processing.SupportedAnnotationTypes; 8 import javax.annotation.processing.SupportedSourceVersion; 9 import javax.lang.model.SourceVersion; 10 import javax.lang.model.element.Element; 11 import javax.lang.model.element.ElementKind; 12 import javax.lang.model.element.TypeElement; 13 import javax.tools.Diagnostic; 14 @SupportedAnnotationTypes("com.mano.annotation.CustomAnnotation") 15 @SupportedSourceVersion(SourceVersion.RELEASE_10) 16 public class CustomAnnotationProcessor extends 17 AbstractProcessor { 18 public CustomAnnotationProcessor() { 19 } public Boolean process(Set<? extends 20 TypeElement> annotations, 21 RoundEnvironment roundEnv) { 22 for (Element e : roundEnv.getElementsAnnotatedWith 23 (CustomAnnotation.class)) { 24 if (e.getKind() != ElementKind.FIELD) { 25 processingEnv.getMessager().printMessage( 26 Diagnostic.Kind.WARNING, 27 "Not a field", e); 28 continue; 29 } 30 } 31 return true; 32 } 33 }
編譯器依賴於兩種服務:診斷偵聽器和文件管理器。如果提供了偵聽器,則將診斷信息提供給偵聽器;否則,將向偵聽器提供診斷信息。否則,診斷將以未指定的格式格式化,並定向到默認的錯誤輸出系統(System.err)。默認情況下,編譯器工具與標准文件管理器關聯,並且可以與滿足其要求的任何其他文件管理器一起正常工作。
注釋處理器
編譯過程還包括注釋處理器。它執行編譯由注釋驅動的代碼的附加過程。處理過程按一系列輪次進行,其中每個輪次處理其上一輪產生的注釋子集。實現注釋過程的接口是javax.annotation.processin.Processor。實現類必須提供一個無參數的構造函數,以供工具實例化處理器。處理基礎結構應遵循某些協議,例如:
- 通過使用處理器類的無參數構造函數實例化注釋處理器。
- 工具通過傳遞適當的ProcessingEnvironmentinstance實例來調用init方法。
- 這些工具調用由Processor接口定義的方法,例如getSupportedAnnotationTypes(), getSupportedOptions(), 和getSupportedSourceVersion()。這些方法在每次運行中調用一次,而不是在每個回合中調用一次。
- 最后,調用Processor對象上的process ()方法。
例如,簡單的注釋可以定義如下:
1 package com.mano.jcapidemo; 2 import java.lang.annotation.ElementType; 3 import java.lang.annotation.Target; 4 @Target(ElementType.FIELD) 5 public@interface CustomAnnotation { 6 }
一個非常簡單的注釋處理器,用於警告將注釋應用於字段以外的任何其他元素,如下所示:
1 package com.mano.jcapidemo; 2 3 import com.mano.annotation.CustomAnnotation; 4 import java.util.Set; 5 import javax.annotation.processing.AbstractProcessor; 6 import javax.annotation.processing.RoundEnvironment; 7 import javax.annotation.processing.SupportedAnnotationTypes; 8 import javax.annotation.processing.SupportedSourceVersion; 9 import javax.lang.model.SourceVersion; 10 import javax.lang.model.element.Element; 11 import javax.lang.model.element.ElementKind; 12 import javax.lang.model.element.TypeElement; 13 import javax.tools.Diagnostic; 14 @SupportedAnnotationTypes("com.mano.annotation.CustomAnnotation") 15 @SupportedSourceVersion(SourceVersion.RELEASE_10) 16 public class CustomAnnotationProcessor extends 17 AbstractProcessor { 18 public CustomAnnotationProcessor() { 19 } public Boolean process(Set<? extends 20 TypeElement> annotations, 21 RoundEnvironment roundEnv) { 22 for (Element e : roundEnv.getElementsAnnotatedWith 23 (CustomAnnotation.class)) { 24 if (e.getKind() != ElementKind.FIELD) { 25 processingEnv.getMessager().printMessage( 26 Diagnostic.Kind.WARNING, 27 "Not a field", e); 28 continue; 29 } 30 } 31 return true; 32 } 33 }
SupportedAnnotationTypes定義注釋處理器將處理哪種類型的注釋,SupportedSourceVersion定義其支持的版本。 我們首先擴展AbstractProcessor抽象類,該類允許我們覆蓋處理方法。 處理方法內部編寫的邏輯完成了所有技巧,這些技巧涉及我們選擇設置哪些標准來處理注釋。 這最終決定了注釋的含義。
元素掃描儀
元素掃描器在編譯過程中對所有語言元素執行分析。它根據訪問者模式構建,以根據源版本的發布情況,以默認行為掃描程序元素。例如,ElementScanner9根據源版本RELEASE_9和RELEASE_10進行掃描,而ElementScanner8分別根據源版本RELEASE_8進行掃描。這兩個類都可以在javax.lang.model.utilpackage中找到。
編譯樹API Compiler Tree API
有時,有必要將整個Java源文件解析為抽象語法樹,尤其是為了進行更深入的分析。Java編譯器樹API遵守該要求,並與javax.lang.model包緊密關聯。它以與元素掃描器相同的模式構建,並且以類似的方式工作。密鑰類稱為TreePathScanner。它訪問所有子樹節點,並有助於維護到父節點的路徑。要訪問特定節點,我們可以簡單地覆蓋相應的visitorXYZ方法。
總結
Java編譯器API從Java應用程序中提供對Java編譯器的編程訪問。顯而易見,此API有更深層的含義,在這里我們只涉及了其中的內容。但是,此快速介紹可能會提供有關在開始使用Java Compiler API時要查找的內容的線索。
參考
Java API文檔