编译器 API 加载Java文件特性
2018-02-28 13:40
281 查看
一.运行时编译java文件在 JDK 6 中,类库通过
获得编译器对象:
通过调用
获得编译器对象之后,可以调用
public void doSomething(){
Date date = new Date(10, 3, 3);
// 这个构造函数被标记为deprecated, 编译时向错误输出输出信息。
System.out.println("Doing");
}
}public class Co
ca21
mpiler {
public static void main(String[] args) throws Exception {
String fullQuanlifiedFileName = "Target.java";
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
FileOutputStream err = new FileOutputStream("err.txt");
int compilationResult = compiler.run(null, null, err, fullQuanlifiedFileName);
if (compilationResult == 0) {
System.out.println("Done");
} else {
System.out.println("Fail");
}
}
}
编译非文本形式的文件JDK 6 的编译器 API 的另外一个强大之处在于,它可以编译的源文件的形式并不局限于文本文件。
在 source path 和/或 CLASSPATH 中查找源文件或者 jar 包;
处理输入,输出文件;
在这个过程中,
Writer out,
JavaFileManager fileManager,
DiagnosticListener<? super JavaFileObject> diagnosticListener,
Iterable<String> options,
Iterable<String> classes,
Iterable<? extends JavaFileObject> compilationUnits
)
方法得到。关于每个参数的含义,请参见 JDK 文档。传递不同的参数,会得到不同的
![](http://www.ibm.com/developerworks/cn/java/j-lo-jse64/CompilationThroughTask1.png)
public class Compiler {
public static void main(String[] args) throws Exception {
String fullQuanlifiedFileName = "Target.java";
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
Iterable<?extendsJavaFileObject>files= fileManager.getJavaFileObjectsFromStrings(Arrays.asList(fullQuanlifiedFileName));
JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null, null, null, files);
Boolean result = task.call();
if (result == true) {
System.out.println("Succeeded");
}
}
}
以上是第一步,通过构造一个
接下来第二步,开发者希望生成
public class StringObject extends SimpleJavaFileObject {
private String contents = null;
public Compiler(String className, String contents) throws Exception {
super(new URI(className), Kind.SOURCE);
this.contents = contents;
}
public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
return contents;
}
}
public class AdvancedCompiler {
// Steps used to compile Calculator
// Steps used to compile StringObject
// construct CalculatorTest in memory
public static void main(String[] args) throws Exception{
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
String contents="public class Target {\r\n" +
" public void doSomething(){\r\n" +
" Date date = new Date(10, 3, 3); \r\n" +
" // 这个构造函数被标记为deprecated, 编译时向错误输出输出信息。\r\n" +
" System.out.println(\"Doing\");\r\n" +
" }\r\n" +
"}";
JavaFileObject file = new StringObject("Target", contents.toString());
Iterable<? extends JavaFileObject> files = Arrays.asList(file);
JavaCompiler.CompilationTask task = compiler.getTask ( null, fileManager, null, null, null, files);
Boolean result = task.call();
if( result == true ) {
System.out.println("Succeeded");
}
}
}
程序在内存中构造了 Target类,并且通过
通过fileManager 获取class文件内容
JavaClassObject jco = fileManager.getMainJavaClassObject();
List<JavaClassObject> innerClassJcos = fileManager.getInnerClassJavaClassObject();
javax.tools包提供了程序运行时调用编译器的 API。从这个包的名字 tools 可以看出,这个开发包提供的功能并不仅仅限于编译器。工具还包括 javah、jar、pack200 等,它们都是 JDK 提供的命令行工具。这个开发包希望通过实现一个统一的接口,可以在运行时调用这些工具。在 JDK 6 中,编译器被给予了特别的重视。针对编译器,JDK 设计了两个接口,分别是
JavaCompiler和
JavaCompiler.CompilationTask。下面给出一个例子,展示如何在运行时调用编译器。指定编译文件名称(该文件必须在 CLASSPATH 中可以找到):
String fullQuanlifiedFileName = "compile" + java.io.File.separator +"Target.java";
获得编译器对象:
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
通过调用
ToolProvider的
getSystemJavaCompiler方法,JDK 提供了将当前平台的编译器映射到内存中的一个对象。这样使用者可以在运行时操纵编译器。
JavaCompiler是一个接口,它继承了
javax.tools.Tool接口。因此,第三方实现的编译器,只要符合规范就能通过统一的接口调用。同时,tools 开发包希望对所有的工具提供统一的运行时调用接口。相信将来,
ToolProvider类将会为更多地工具提供
getSystemXXXTool方法。tools 开发包实际为多种不同工具、不同实现的共存提供了框架。编译文件:
int result = compiler.run(null, null, null, fileToCompile);
获得编译器对象之后,可以调用
Tool.run方法对源文件进行编译。
Run方法的前三个参数,分别可以用来重定向标准输入、标准输出和标准错误输出,
null值表示使用默认值。参考:public class Target {
public void doSomething(){
Date date = new Date(10, 3, 3);
// 这个构造函数被标记为deprecated, 编译时向错误输出输出信息。
System.out.println("Doing");
}
}public class Co
ca21
mpiler {
public static void main(String[] args) throws Exception {
String fullQuanlifiedFileName = "Target.java";
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
FileOutputStream err = new FileOutputStream("err.txt");
int compilationResult = compiler.run(null, null, err, fullQuanlifiedFileName);
if (compilationResult == 0) {
System.out.println("Done");
} else {
System.out.println("Fail");
}
}
}
编译非文本形式的文件JDK 6 的编译器 API 的另外一个强大之处在于,它可以编译的源文件的形式并不局限于文本文件。
JavaCompiler类依靠文件管理服务可以编译多种形式的源文件。比如直接由内存中的字符串构造的文件,或者是从数据库中取出的文件。这种服务是由
JavaFileManager类提供的。通常的编译过程分为以下几个步骤:解析 javac 的参数;
在 source path 和/或 CLASSPATH 中查找源文件或者 jar 包;
处理输入,输出文件;
在这个过程中,
JavaFileManager类可以起到创建输出文件,读入并缓存输出文件的作用。由于它可以读入并缓存输入文件,这就使得读入各种形式的输入文件成为可能。JDK 提供的命令行工具,处理机制也大致相似,在未来的版本中,其它的工具处理各种形式的源文件也成为可能。为此,新的 JDK 定义了
javax.tools.FileObject和
javax.tools.JavaFileObject接口。任何类,只要实现了这个接口,就可以被
JavaFileManager识别。如果要使用
JavaFileManager,就必须构造
CompilationTask。JDK 6 提供了
JavaCompiler.CompilationTask类来封装一个编译操作。这个类可以通过:JavaCompiler.getTask (
Writer out,
JavaFileManager fileManager,
DiagnosticListener<? super JavaFileObject> diagnosticListener,
Iterable<String> options,
Iterable<String> classes,
Iterable<? extends JavaFileObject> compilationUnits
)
方法得到。关于每个参数的含义,请参见 JDK 文档。传递不同的参数,会得到不同的
CompilationTask。通过构造这个类,一个编译过程可以被分成多步。进一步,
CompilationTask提供了
setProcessors(Iterable<? extends Processor>processors)方法,用户可以制定处理 annotation 的处理器。图 1 展示了通过
CompilationTask进行编译的过程:
![](http://www.ibm.com/developerworks/cn/java/j-lo-jse64/CompilationThroughTask1.png)
public class Compiler {
public static void main(String[] args) throws Exception {
String fullQuanlifiedFileName = "Target.java";
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
Iterable<?extendsJavaFileObject>files= fileManager.getJavaFileObjectsFromStrings(Arrays.asList(fullQuanlifiedFileName));
JavaCompiler.CompilationTask task = compiler.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的一个测试类,而不是手工编写。使用 compiler API,可以将内存中的一段字符串,编译成一个 CLASS 文件。
public class StringObject extends SimpleJavaFileObject {
private String contents = null;
public Compiler(String className, String contents) throws Exception {
super(new URI(className), Kind.SOURCE);
this.contents = contents;
}
public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
return contents;
}
}
SimpleJavaFileObject是
JavaFileObject的子类,它提供了默认的实现。继承
SimpleJavaObject之后,只需要实现
getCharContent方法。接下来,在内存中构造
Calculator的测试类
CalculatorTest,并将代表该类的字符串放置到
StringObject中,传递给
JavaCompiler的
getTask方法。
public class AdvancedCompiler {
// Steps used to compile Calculator
// Steps used to compile StringObject
// construct CalculatorTest in memory
public static void main(String[] args) throws Exception{
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
String contents="public class Target {\r\n" +
" public void doSomething(){\r\n" +
" Date date = new Date(10, 3, 3); \r\n" +
" // 这个构造函数被标记为deprecated, 编译时向错误输出输出信息。\r\n" +
" System.out.println(\"Doing\");\r\n" +
" }\r\n" +
"}";
JavaFileObject file = new StringObject("Target", contents.toString());
Iterable<? extends JavaFileObject> files = Arrays.asList(file);
JavaCompiler.CompilationTask task = compiler.getTask ( null, fileManager, null, null, null, files);
Boolean result = task.call();
if( result == true ) {
System.out.println("Succeeded");
}
}
}
程序在内存中构造了 Target类,并且通过
StringObject的构造函数,将内存中的字符串,转换成了
JavaFileObject对象。
通过fileManager 获取class文件内容
JavaClassObject jco = fileManager.getMainJavaClassObject();
List<JavaClassObject> innerClassJcos = fileManager.getInnerClassJavaClassObject();
相关文章推荐
- atitit.跨语言实现备份mysql数据库 为sql文件特性 api 兼容性java c#.net php js
- 利用Java注解特性加载属性文件(properties)的值到Java类
- Jakarta POI 3.0—访问微软Office格式文件的Java API
- Java SE 6 新特性: 编译器 API
- Java 类加载器(二)-类加载器的特性
- Java SE 6 新特性: 编译器 API
- Java 6新特性一箩筐,将提供中文化的API文档
- Java SE 6 新特性: 编译器 API
- java文件加载
- Java和Eclipse中加载本地库(.dll文件)
- Java 编译器API
- eclipse编译器编码与jsp java 等文件编码一些自己的理解
- java 动态编译.java文件,然后动态加载运行
- Java应用程序中加载位图文件的逐步指南
- Java加载XML文件
- 0day-java load dynamic library from any path(java加载任意目录库文件)
- Java应用程序中加载位图文件的逐步指南
- jsp里使用java excel api实现文件下载
- JAVA学习提高之----使用J2SE API读properties文件的六种方法
- tomcat中编译器的版本过低,导致不支持java 5以上的新特性