知识大全 Java SE 6 新特性: 编译器 API

Posted 文件

篇首语:高斋晓开卷,独共圣人语。本文由小常识网(cha138.com)小编为大家整理,主要介绍了知识大全 Java SE 6 新特性: 编译器 API相关的知识,希望对你有一定的参考价值。

Java SE 6 新特性: 编译器 API  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!

   年底 Sun 公司发布了 Java Standard Edition (Java SE )的最终正式版 代号 Mustang(野马) 跟 Tiger(Java SE )相比 Mustang 在性能方面有了不错的提升 与 Tiger 在 API 库方面的大幅度加强相比 虽然 Mustang 在 API 库方面的新特性显得不太多 但是也提供了许多实用和方便的功能 在脚本 WebService XML 编译器 API 数据库 JMX 网络和 Instrumentation 方面都有不错的新特性和功能加强 本系列 文章主要介绍 Java SE 在 API 库方面的部分新特性 通过一些例子和讲解 帮助开发者在编程实践当中更好的运用 Java SE 提高开发效率

  本文是其中的第四篇 介绍了 JDK 中为在运行时操纵编译器所增加的编译器 API(JSR ) 您将了解到 利用此 API 开发人员可以在运行时调用 Java 编译器 还可以编译非文本形式的 Java 源代码 最后还能够采集编译器的诊断信息 本文将展开描述这些功能 并使用这些功能构造一个简单的应用 —— 在内存中 直接为一个类生成测试用例

  新API 功能简介

  JDK 提供了在运行时调用编译器的 API 后面我们将假设把此 API 应用在 JSP 技术中 在传统的 JSP 技术中 服务器处理 JSP 通常需要进行下面 个步骤

    分析 JSP 代码 生成 Java 代码 将 Java 代码写入存储器 启动另外一个进程并运行编译器编译 Java 代码 将类文件写入存储器 服务器读入类文件并运行

  但如果采用运行时编译 可以同时简化步骤 和 节约新进程的开销和写入存储器的输出开销 提高系统效率 实际上 在 JDK 中 Sun 也提供了调用编译器的编程接口 然而不同的是 老版本的编程接口并不是标准 API 的一部分 而是作为 Sun 的专有实现提供的 而新版则带来了标准化的优点

  新API 的第二个新特性是可以编译抽象文件 理论上是任何形式的对象 —— 只要该对象实现了特定的接口 有了这个特性 上述例子中的步骤 也可以省略 整个 JSP 的编译运行在一个进程中完成 同时消除额外的输入输出操作

  第三个新特性是可以收集编译时的诊断信息 作为对前两个新特性的补充 它可以使开发人员轻松的输出必要的编译错误或者是警告信息 从而省去了很多重定向的麻烦

  运行时编译 Java 文件

  在 JDK 中 类库通过 javax tools 包提供了程序运行时调用编译器的 API 从这个包的名字 tools 可以看出 这个开发包提供的功能并不仅仅限于编译器 工具还包括 javah jar pack 等 它们都是 JDK 提供的命令行工具 这个开发包希望通过实现一个统一的接口 可以在运行时调用这些工具 在 JDK 中 编译器被给予了特别的重视 针对编译器 JDK 设计了两个接口 分别是 JavaCompiler 和 JavaCompiler CompilationTask

  下面给出一个例子 展示如何在运行时调用编译器

指定编译文件名称(该文件必须在 CLASSPATH 中可以找到) String fullQuanlifiedFileName = pile + java io File separator + Target java ; 获得编译器对象 JavaCompiler piler = ToolProvider getSystemJavaCompiler();

  通过调用 ToolProvider 的 getSystemJavaCompiler 方法 JDK 提供了将当前平台的编译器映射到内存中的一个对象 这样使用者可以在运行时操纵编译器 JavaCompiler 是一个接口 它继承了 javax tools Tool 接口 因此 第三方实现的编译器 只要符合规范就能通过统一的接口调用 同时 tools 开发包希望对所有的工具提供统一的运行时调用接口 相信将来 ToolProvider 类将会为更多地工具提供 getSystemXXXTool 方法 tools 开发包实际为多种不同工具 不同实现的共存提供了框架

编译文件 int result = piler run(null null null fileToCompile);

  获得编译器对象之后 可以调用 Tool run 方法对源文件进行编译 Run 方法的前三个参数 分别可以用来重定向标准输入 标准输出和标准错误输出 null 值表示使用默认值 清单 给出了一个完整的例子

  清单 程序运行时编译文件

   package pile; import java util Date; public class Target public void doSomething() Date date = new Date( ); // 这个构造函数被标记为deprecated 编译时会 // 向错误输出输出信息 System out println( Doing ); package pile; import javax tools *; import java io FileOutputStream; public class Compiler public static void main(String[] args) throws Exception String fullQuanlifiedFileName = pile + java io File separator + Target java ; JavaCompiler piler = ToolProvider getSystemJavaCompiler(); FileOutputStream err = new FileOutputStream( err txt ); int pilationResult = piler run(null null err fullQuanlifiedFileName); if(pilationResult == ) System out println( Done ); else System out println( Fail );

  首 先运行 <JDK _INSTALLATION_DIR>\\bin\\javac Compiler java 然后运行 <JDK _INSTALLATION_DIR>\\jdk \\bin\\java pile Compiler 屏幕上将输出 Done 并会在当前目录生成一个 err txt 文件 文件内容如下

  

  Note: pile/Target java uses or overrides a deprecated API Note: Repile with Xlint:deprecation for details

  仔细观察 run 方法 可以发现最后一个参数是 String arguments 是一个变长的字符串数组 它的实际作用是接受传递给 javac 的参数 假设要编译 Target java 文件 并显示编译过程中的详细信息 命令行为 javac Target java verbose 相应的可以将 句改为

  

  int pilationResult = piler run(null null err verbose fullQuanlifiedFileName);

    编译非文本形式的文件

  JDK 的编译器 API 的另外一个强大之处在于 它可以编译的源文件的形式并不局限于文本文件 JavaCompiler 类依靠文件管理服务可以编译多种形式的源文件 比如直接由内存中的字符串构造的文件 或者是从数据库中取出的文件 这种服务是由 JavaFileManager 类提供的 通常的编译过程分为以下几个步骤

    解析 javac 的参数 在 source path 和/或 CLASSPATH 中查找源文件或者 jar 包 处理输入 输出文件

  在这个过程中 JavaFileManager 类可以起到创建输出文件 读入并缓存输出文件的作用 由于它可以读入并缓存输入文件 这就使得读入各种形式的输入文件成为可能 JDK 提供的命令行工具 处理机制也大致相似 在未来的版本中 其它的工具处理各种形式的源文件也成为可能 为此 新的 JDK 定义了 javax tools FileObject 和 javax tools JavaFileObject 接口 任何类 只要实现了这个接口 就可以被 JavaFileManager 识别

  如果要使用 JavaFileManager 就必须构造 CompilationTask JDK 提供了 JavaCompiler CompilationTask 类来封装一个编译操作 这个类可以通过

  

  JavaCompiler getTask ( Writer out JavaFileManager fileManager DiagnosticListener<? super JavaFileObject> diagnosticListener Iterable<String> options Iterable<String> classes Iterable<? extends JavaFileObject> pilationUnits)

  方法得到 关于每个参数的含义 请参见 JDK 文档 传递不同的参数 会得到不同的 CompilationTask 通过构造这个类 一个编译过程可以被分成多步 进一步 CompilationTask 提供了 setProcessors(Iterable<? extends Processor>processors) 方法 用户可以制定处理 annotation 的处理器 图 展示了通过 CompilationTask 进行编译的过程

  图 使用 CompilationTask 进行编译

  

  下面的例子通过构造 CompilationTask 分多步编译一组 Java 源文件

  清单 构造 CompilationTask 进行编译

   package math; public class Calculator public int multiply(int multiplicand int multiplier) return multiplicand * multiplier; package pile; import javax tools *; import java io FileOutputStream; import java util Arrays; public class Compiler public static void main(String[] args) throws Exception String fullQuanlifiedFileName = math + java io File separator + Calculator java ; JavaCompiler piler = ToolProvider getSystemJavaCompiler(); StandardJavaFileManager fileManager = piler getStandardFileManager(null null null); Iterable<? extends JavaFileObject> files = fileManager getJavaFileObjectsFromStrings( Arrays asList(fullQuanlifiedFileName)); JavaCompiler CompilationTask task = piler getTask( null fileManager null null null files); Boolean result = task call(); if( result == true ) System out println( Succeeded );

  以上是第一步 通过构造一个 CompilationTask 编译了一个 Java 文件 行实现了主要逻辑 第 行 首先取得一个编译器对象 由于仅仅需要编译普通文件 因此第 行中通过编译器对象取得了一个标准文件管理器 行 将需要编译的文件构造成了一个 Iterable 对象 最后将文件管理器和 Iterable 对象传递给 JavaCompiler 的 getTask 方法 取得了 JavaCompiler CompilationTask 对象

  接下来第二步 开发者希望生成 Calculator 的一个测试类 而不是手工编写 使用 piler API 可以将内存中的一段字符串 编译成一个 CLASS 文件

  清单 定制 JavaFileObject 对象

   package math; import URI; public class StringObject extends SimpleJavaFileObject private String contents = null; public StringObject(String className String contents) throws Exception super(new URI(className) Kind SOURCE); ntents = contents; public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException return contents;

  SimpleJavaFileObject 是 JavaFileObject 的子类 它提供了默认的实现 继承 SimpleJavaObject 之后 只需要实现 getCharContent 方法 如 清单 中的 行所示 接下来 在内存中构造 Calculator 的测试类 CalculatorTest 并将代表该类的字符串放置到 StringObject 中 传递给 JavaCompiler 的 getTask 方法 清单 展现了这些步骤

  清单 编译非文本形式的源文件

   package math; import javax tools *; import java io FileOutputStream; import java util Arrays; public class AdvancedCompiler public static void main(String[] args) throws Exception // Steps used to pile Calculator // Steps used to pile StringObject // construct CalculatorTest in memory JavaCompiler piler = ToolProvider getSystemJavaCompiler(); StandardJavaFileManager fileManager = piler getStandardFileManager(null null null); JavaFileObject file = constructTestor(); Iterable<? extends JavaFileObject> files = Arrays asList(file); JavaCompiler CompilationTask task = piler getTask ( null fileManager null null null files); Boolean result = task call(); if( result == true ) System out println( Succeeded ); private static SimpleJavaFileObject constructTestor() StringBuilder contents = new StringBuilder( package math; + class CalculatorTest \\n + public void testMultiply() \\n + Calculator c = new Calculator();\\n + System out println(c multiply( ));\\n + \\n + public static void main(String[] args) \\n + CalculatorTest ct = new CalculatorTest();\\n + ct testMultiply();\\n + \\n + \\n ); StringObject so = null; try so = new StringObject( math CalculatorTest contents toString()); catch(Exception exception) exception printStackTrace(); return so;

  实现逻辑和 清单 相似 不同的是在 行 程序在内存中构造了 CalculatorTest 类 并且通过 StringObject 的构造函数 将内存中的字符串 转换成了 JavaFileObject 对象

  采集编译器的诊断信息

  第三个新增加的功能 是收集编译过程中的诊断信息 诊断信息 通常指错误 警告或是编译过程中的详尽输出 JDK 通过 Listener 机制 获取这些信息 如果要注册一个 DiagnosticListener 必须使用 CompilationTask 来进行编译 因为 Tool 的 run 方法没有办法注册 Listener 步骤很简单 先构造一个 Listener 然后传递给 JavaFileManager 的构造函数 清单 对 清单 进行了改动 展示了如何注册一个 DiagnosticListener

  清单 注册一个 DiagnosticListener 收集编译信息

   package math; public class Calculator public int multiply(int multiplicand int multiplier) return multiplicand * multiplier // deliberately omit semicolon ADiagnosticListener // will take effect package pile; import javax tools *; import java io FileOutputStream; import java util Arrays; public class CompilerWithListener public static void main(String[] args) throws Exception String fullQuanlifiedFileName = math + java io File separator + Calculator java ; JavaCompiler piler = ToolProvider getSystemJavaCompiler(); StandardJavaFileManager fileManager = piler getStandardFileManager(null null null); Iterable<? extends JavaFileObject> files = fileManager getJavaFileObjectsFromStrings( Arrays asList(fullQuanlifiedFileName)); DiagnosticCollector<JavaFileObject> collector = new DiagnosticCollector<JavaFileObject>(); JavaCompiler CompilationTask task = piler getTask(null fileManager collector null null files); Boolean result = task call(); List<Diagnostic<? extends JavaFileObject>> diagnostics = collector getDiagnostics(); for(Diagnostic<? extends JavaFileObject> d : diagnostics) System out println( Line Number > + d getLineNumber()); System out println( Message > + d getMessage(Locale ENGLISH)); System out println( Source + d getCode()); System out println( \\n ); if( result == true ) System out println( Succeeded );

  在 行 构造了一个 DiagnosticCollector 对象 这个对象由 JDK 提供 它实现了 DiagnosticListener 接口 行将它注册到 CompilationTask 中去 一个编译过程可能有多个诊断信息 每一个诊断信息 被抽象为一个 Diagnostic 行 将所有的诊断信息逐个输出 编译并运行 Compiler 得到以下输出

  清单 DiagnosticCollector 收集的编译信息

  Line Number > Message >math/Calculator java: : ; expectedSource >piler err expected

  实际上 也可以由用户自己定制 清单 给出了一个定制的 Listener

  清单 自定义的 DiagnosticListener

   class ADiagnosticListener implements DiagnosticListener<JavaFileObject> public void report(Diagnostic<? extends JavaFileObject> diagnostic) System out println( Line Number > + diagnostic getLineNumber()); System out println( Message > + diagnostic getMessage(Locale ENGLISH)); System out println( Source + diagnostic getCode()); System out println( \\n );

  总结

cha138/Article/program/Java/JSP/201311/19606

相关参考

知识大全 Java SE6调用Java编译器的两种新方法

JavaSE6调用Java编译器的两种新方法  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!&nbs

知识大全 Java SE 6 新特性: Instrumentatio

JavaSE6新特性:Instrumentatio  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! 

知识大全 Java SE 6 新特性: Java DB&n

JavaSE6新特性:JavaDB&n  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  年底Sun

知识大全 Java SE 6 新特性: HTTP 增强

JavaSE6新特性:HTTP增强  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  摘要  Jav

知识大全 驾驭“野马”-- 探索Java SE 6的一些新特性

驾驭“野马”--探索JavaSE6的一些新特性  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  s

知识大全 Java SE 6 新特性: JMX 与系统管理

JavaSE6新特性:JMX与系统管理  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  年底Sun

知识大全 Java SE 6 新特性: 对脚本语言的支持

JavaSE6新特性:对脚本语言的支持  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  年底Sun

知识大全 SE6调用编译器的两种方法[2]

Java进阶:SE6调用编译器的两种方法[2]  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  三

知识大全 SE6调用编译器的两种方法[1]

Java进阶:SE6调用编译器的两种方法[1]  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  在

知识大全 在SE6中调用编译器的两种方法[4]

Java进阶:在SE6中调用编译器的两种方法[4]  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!