今天给你们分享的是Java编译器API简介,文章部份内容摘自【优锐课】学习笔记。java
Java编译器API是Java模块(称为java.compiler)的一部分。该模块包括语言模型和注释处理,以及编译器API。它定义了Java编程语言和编译器工具的类型和模型声明,能够在执行期间从应用程序代码中调用它们。注释处理有助于访问注释处理器,能够将其视为Java编译器的插件。它使注释处理器和注释处理工具环境之间可以通讯。模型,元素和类型包处理Java编程语言的元素,而util包则帮助处理程序元素和类型。编程
javax.tools包提供了与Java编译器一块儿使用的接口和类,而且能够在执行期间从程序中调用它。 它提供了一个框架,该框架容许客户端从其本身的应用程序代码定位和运行编译器。它还提供了服务提供者接口(SPI),用于对诊断的结构化访问和用于覆盖文件访问的文件抽象。ToolProvider类提供了编译器API的入口点。此类提供了一些方法来定位编译器的工具提供者。 例如,咱们能够轻松地找到系统中安装的编译器支持的Java源版本列表。api
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文件放入类路径中,并按如下方式定位它:dom
1 JavaCompiler vendorJavaCompiler = 2 ServiceLoader.load(JavaCompiler.class).iterator().next();
ServiceProvider是Java的util类之一,用于查找和加载部署在执行环境中的服务提供者。编程语言
找到JavaCompiler后,就能够经过Java源代码执行各类编译诊断任务。为了说明这个想法,让咱们首先建立一个简单的类,以下所示:ide
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。实现类必须提供一个无参数的构造函数,以供工具实例化处理器。处理基础结构应遵循某些协议,例如:
例如,简单的注释能够定义以下:
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中找到。
有时,有必要将整个Java源文件解析为抽象语法树,尤为是为了进行更深刻的分析。Java编译器树API遵照该要求,并与javax.lang.model包紧密关联。它以与元素扫描器相同的模式构建,而且以相似的方式工做。密钥类称为TreePathScanner。它访问全部子树节点,并有助于维护到父节点的路径。要访问特定节点,咱们能够简单地覆盖相应的visitorXYZ方法。
Java编译器API从Java应用程序中提供对Java编译器的编程访问。显而易见,此API有更深层的含义,在这里咱们只涉及了其中的内容。可是,此快速介绍可能会提供有关在开始使用Java Compiler API时要查找的内容的线索。
Java API文档