(本文翻译自Core Java Technology Tech Tips for April 2007 ) 起 初,标准java平台没有提供标准的接口来调用其自身的编译器生成Java字节码。在sun提供的平台实现上,用户可以通过一个非标准的 com.sun.tools.javac包来编译自己的代码。但是这个包并不能提供标准的、开放的编程借口。使用其他java平台实现的用户不能访问这个 类。使用java se 6和它的java compiler API (由JSR-199定义),你可以从自己的应用中直接调用javac编译工具。 有两种方法来使用这个工具。一种相对简单,另外一种要复杂一些但是允许你处理更多的要求。你可以首先使用简单的方法来编译下面的“hello world“程序: public class Hello { public static void main System.out.println("Hello, World"); } }
为了从你的程序中调用java编译工具,你需要使用JavaCompiler借口。使用这个借口,你可以设置源代码路径,classpath,还有目的路径。通过将每个待编译的文件作为一个个JavaFileObject实例,你可以编译它 们。暂时你还不需要理解这个JavaFileObject。 使用ToolProvider类来获得JavaCompiler接口的一个默认实现。ToolProvider类提供一个getSystemJavaCompiler()方法,返回JavaCompiler接口的一个实例。 JavaCompiler compiler=ToolProvider.getSystemJavaCompiler(); 使用JavaCompiler最简单的方法是直接调用run()方法,run()方法是在Tool接口中实现的,其声明为: int run(InputStream in, OutputStream out, OutputStream err, String... arguments) 传递null作为其中的流参数会使用默认的 所以,为了编译前面提出的Hello类,你只需要: int results = tool.run(null, null, null, "Hello.java"); 假如没有错误的话,这段代码会在目标文件夹里产生一个Hello.class文件。如果出错,run()方法会将消息输出到标准错误流(System.err)中,也就是函数中第三个参数指定的流。函数出错时会返回一个非0值。 你可以使用下面的代码来编译Hello.java文件: 当你编译好上面的CompileIt程序后,你可以多次使用它来重新编译Hello.java文件,而不需要一再地重新编译CompileIt文件。如果没有出错,运行CompileIt输出如下: > java CompileIt Result code: 0 运行CompileIt会在相同的文件夹里产生一个Hello.class文件: 读 到这里,你可以停下来了,因为上面的这些东西已经足够你使用这个标准的编译器了,但是还有更多。如果你想要更好地获得结果,你还有第二种方法来使用编译 器。准确地说,这第二种方法允许开发者使用一种更加好看的表达方式来显示编译的结果,而不仅仅是传递一段错误信息到stderr。这个方法利用了 StandardJavaFileManager类的优点。这个文件管理器提供了一种方法来完成普通文件的输入输出工作。同时在一个 DiagnosticListener实例的帮助下报告编译的诊断信息。后面将要用到的DiagnosticCollector类只是前面那个 listener的一个实现。 在确定什么东西是需要编译的之前,你需要一个文件管理器。创建一个文件管理器需要两个基本的步骤:创建一个 DiagnosticCollector<JavaFileObj new DiagnosticCollector StandardJavaFileManager compiler.getStandardFileManager(diagnostics, aLocale, aCharset 你也可以给这个调用提供一个null的diagnostics listener,但这样只是跟前面的编译器方法一样的效果(dmshi:也就是说实用系统默认值)。 在细致地讨论 JavaCompiler.CompilationTask getTask( Writer out, JavaFileManager fileManager DiagnosticListener<? super Iterable<String> options, Iterable<String> classes, Iterable<? extends JavaFile 大部分的参数都可以为null,他们都有默认值: 最后一个参数 Iterable<? extends JavaFileObje Iterable<? extends File> Iterable<? extends JavaFileObje Iterable<String> names) 事实上,任何满足Iterable的对象都可以用来指明所需要编译对象的集合,而不仅仅是List。这里使用List仅仅是因为它最容易创建^_^: String[] filenames = ...; Iterable<? extends JavaFileObje fileManager.getJavaFileObjectsF 现在你拥有足够的信息来编译你的源文件。getTask()方法返回的 JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null, null Boolean success = task.call(); 假定没有编译错误发生,call()方法会编译所有由 在展示例子之前,我们还有最有一个对象需要了解: 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)); } 最后,你可以调用文件管理器的 将上面所有的整理起来得到下面的程序: import java.io.*; import java.util.*; import javax.tools.*; public class BigCompile { public static void main JavaCompiler compiler = ToolProvider.getSystemJavaCompi DiagnosticCollector new DiagnosticCollector StandardJavaFileManager compiler.getStandardFileManager(diagnostics, null, null); Iterable<? extends JavaFile fileManager.getJavaFileObjectsF JavaCompiler.CompilationTask task = compiler.getTask( null, fileManager, Boolean success = task.call(); for (Diagnostic diagnostic System.console().printf( "Code: %s%n" + "Kind: %s%n" + "Position: %s%n" + "Start Position: %s "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)); } fileManager.close(); System.out.println("Success: " + success); } } 编译执行上面的程序会输出下面的成功信息: > javac BigCompile.java > java BigCompile Success: true 如果程序出错,比如将 > java BigCompile Code: compiler.err.cant.resolve Kind: ERROR Position: 80 Start Position: 70 End Position: 88 Source: Hello.java Message: Hello.java:3: cannot find symbol symbol : method pritnln(java.lang.String) location: class java.io.PrintStream Success: false 使 用Compiler API,你可以做的事情远远不止上面所简单描述的内容。例如,你可以控制输入和输出的文件夹,或者在一个IDE里面实现编译错误的高亮处理。Now, thanks to the Java Compiler API, you can do all that with standard API calls. For more information on the Java Compiler API and JSR 199, see the JSR 199 specification. |