【0】README
0.1)以下内容转自: http://suntips.iteye.com/blog/69002
0.2)for basic java compiler API, please visit http://blog.csdn.net/pacosonswjtu/article/details/50718494
更特别的是,这第二种方式允许开发者将编译输出结果用一种更有意义的方式表现出来,而不是简单的那种送往stdeer的错误文本. 利用 StandardJavaFileManager
类我们有这种更好的途径使用编译器. 这个文件管理器提供了一种方式,用来处理普通文件的输入输出操作. 它同时利用 DiagnosticListener
实例来报告调试信息. 你需要使用的 DiagnosticCollector
类其实是监听器的一种实现.
2)在搞清楚你需要编译什么之前,你需要一个文件管理器. 生成一个管理器基本上需要两步: 创建一个DiagnosticCollector
和 使用 JavaCompiler
的 getStandardFileManager()
方法获得一个文件管理器. 把DiagnosticListener
对象传入 getStandardFileManager()
方法中. 这个监听器可以报告一些非致命的问题,到后来你可以选择性的通过把它传入 getTask()
方法来和编译器共享.
DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, aLocale, aCharset);
3)你也可以往这个调用里传入一个 null 值的诊断器,但这样也就等于用以前的编译器方法了.
3.1)在详细查看 StandardJavaFileManager
之前 ,编译过程涉及到 JavaCompiler
的一个方法叫做 getTask()
. 它有六个参数,返回一个叫做 CompilationTask
内部类的实例:
JavaCompiler.CompilationTask getTask(
Writer out,
JavaFileManager fileManager,
DiagnosticListener<? super JavaFileObject> diagnosticListener,
Iterable<String> options,
Iterable<String> classes,
Iterable<? extends JavaFileObject> compilationUnits)
3.2)缺省情况下,大部分它的参数可以是 null.
* out: System.err
* fileManager: compiler's standard file manager
* diagnosticListener: compiler's default behavior
* options: no command-line options to compiler
* classes: no class names for annotation processing
3.3)最后一个参数 compilationUnits
却是不能够为null ,因为它是你要去编译的东西. 它把我们又带回了StandardJavaFileManager
类.注意这个参数类型: Iterable<?
extends JavaFileObject>
. StandardJavaFileManager
有两个方法返回这样的结果. 你可以使用一个文件对象的List或者String
对象的List,用它们来表示文件名:
Iterable<? extends JavaFileObject> getJavaFileObjectsFromFiles(Iterable<? extends File> files)
Iterable<? extends JavaFileObject> getJavaFileObjectsFromStrings( Iterable<String> names)
3.4)并不仅仅 List
,实际上,任何一个能够标识需要编译的内容的集合的 Iterable
都可以. List
出现在这里只是因为它容易生成:
String[] filenames = ...;
Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjectsFromFiles(Arrays.asList(filenames));
3.5)现在你有了编译源文件的所有的必要的信息. 从 getTask(
) 返回的 JavaCompiler.CompilationTask
实现了Callable
.接口 这样,想让任务开始就去调用call()方法.
JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null, null, null, compilationUnits);
Boolean success = task.call();
4)如果没有编译警告和错误,这个call() 方法会编译所有的 compilationUnits
变量指定的文件,以及有依赖关系的可编译的文件.
想要知道是否所有的都成功了,去查看一下返回的 Boolean
值. 只有当所有的编译单元都执行成功了,这个 call()
方法才返回 Boolean.TRUE
. 一旦有任何错误,这个方法就会返回 Boolean.FALSE
.
在展示运行这个例子之前,让我们添加最后一个东西,DiagnosticListener
, 或者更确切的说, DiagnosticCollector
.的实现类.把这个监听器当作getTask()的第三个参数传递进去,你就可以在编译之后进行一些调式信息的查询了.
for (Diagnostic diagnostic : diagnostics.getDiagnostics()) {
System.console().printf(
"Code: %s%n" +
"Kind: %s%n" +
"Position: %s%n" +
"Start Position: %s%n" +
"End Position: %s%n" +
"Source: %s%n" +
"Message: %s%n",
diagnostic.getCode(), diagnostic.getKind(),
diagnostic.getPosition(), diagnostic.getStartPosition(),
diagnostic.getEndPosition(), diagnostic.getSource(),
diagnostic.getMessage(null));
}
5)在最后,你应该调用管理器的close()
方法.
6)把所有的放在一起,就得到的了下面的程序,让我们重新编译Hello(StandardJavaFileManagerTest)类.
package com.corejava.chapter10_2;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Locale;
import javax.tools.Diagnostic;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
public class AdvancedJavaCompiler
{
public static void main(String[] args) throws IOException
{
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); // 返回java 编译器
// DiagnosticCollector 是监听器的一种实现
DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();
// java 文件管理器
StandardJavaFileManager manager = compiler.getStandardFileManager(diagnostics, Locale.CHINA, Charset.forName("UTF-8"));
/* Iterable<? extends JavaFileObject> getJavaFileObjectsFromFiles(Iterable<? extends File> files)
Iterable<? extends JavaFileObject> getJavaFileObjectsFromStrings(Iterable<String> names) */
// 所要编译的源文件
Iterable<? extends JavaFileObject> compilationUnits = manager.getJavaFileObjects("com/corejava/chapter10_2/StandardJavaFileManagerTest.java");
CompilationTask task = compiler.getTask(null, manager, diagnostics, null, null, compilationUnits);
// 如果没有编译警告和错误,这个call() 方法会编译所有的 compilationUnits 变量指定的文件,以及有依赖关系的可编译的文件.
Boolean suc = task.call();
/* 只有当所有的编译单元都执行成功了,这个 call() 方法才返回 Boolean.TRUE . 一旦有任何错误,这个方法就会返回 Boolean.FALSE .
* 在展示运行这个例子之前,让我们添加最后一个东西,DiagnosticListener , 或者更确切的说, DiagnosticCollector .的实现类.
* 把这个监听器当作getTask()的第三个参数传递进去,你就可以在编译之后进行一些调式信息的查询了. */
for(Diagnostic diagnostic : diagnostics.getDiagnostics())
{
System.console().printf(
"Code: %s%n" +
"Kind: %s%n" +
"Position: %s%n" +
"Start Position: %s%n" +
"End Position: %s%n" +
"Source: %s%n" +
"Message: %s%n",
diagnostic.getCode(), diagnostic.getKind(),
diagnostic.getPosition(), diagnostic.getStartPosition(),
diagnostic.getEndPosition(), diagnostic.getSource(),
diagnostic.getMessage(null));
}
manager.close();
System.out.println("success : " + suc);
}
}
7)然而,如果你把 println
方法改成书写错误的 pritnln
方法,当你运行时你会得到下面的信息:
E:\bench-cluster\cloud-data-preprocess\CoreJavaAdvanced\src>java com.corejava.chapter10_2.AdvancedJa
vaCompiler
Code: compiler.err.cant.resolve.location.args
Kind: ERROR
Position: 139
Start Position: 129
End Position: 147
Source: RegularFileObject[com\corejava\chapter10_2\StandardJavaFileManagerTest.java]
Message: 找不到符号
符号: 方法 printnl(java.lang.String)
位置: 类型为java.io.PrintStream的变量 out
success : false
Attention) for source code about instances above, please visit https://github.com/pacosonTang/core-java-volume/tree/master/coreJavaAdvanced/chapter10/10_2
使用Compiler API,你可以实现比在这篇简要的提示介绍的更多的事情. 例如,你可以控制输入输出的目录或者在集成编译器里高亮一些编译错误. 现在,向 Java Compiler API表示感谢,你可以使用标准API了. For more information on the Java Compiler API and JSR 199, see the JSR 199 specification.