请完整阅读java使用命令编译打包
文档后再继续阅读。
关键组件介绍:
- JavaCompiler - 表示java编译器, run方法执行编译操作. 或者可以先生成编译任务(CompilationTask), 然后与调用任务的call方法执行
- JavaFileObject - 表示一个java源文件对象
- JavaFileManager - Java源文件管理类, 管理一系列JavaFileObject
- Diagnostic - 表示一个诊断信息
- DiagnosticListener - 诊断信息监听器, 编译过程触发. 生成编译task(
JavaCompiler#getTask()
)或获取FileManager(JavaCompiler#getStandardFileManager()
)时需要传递DiagnosticListener以便收集诊断信息
读取本地的.java
文件,动态编译为.class
文件后保存在磁盘上。
第一种方式
public static void main(String[] args) {
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
// 输入流、输出流、错误流、javac参数
int result = compiler.run(null, null, null, "/path/to/source");
if (result != 0) {
System.out.println("编译失败");
}
}
通过以上的方式,默认编译的文件和源文件存在同一目录下。可以通过-d
参数指定编译输出位置。
第二种方式
public static void main(String[] args) {
// 获取java编译器
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
// 获取文件管理器
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
// 定义要编译的源文件
File file = new File("F:\\javalearning\\src\\com\\feb13th\\demo1\\HelloWorld.java");
//通过源文件获取到要编译的Java类源码迭代器,包括所有内部类,其中每个类都是一个 JavaFileObject,也被称为一个汇编单元
Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjects(file);
// options -d 指定编译后的文件位置, -classpath 指定依赖
Iterable<String> options = Arrays.asList(
"-d", "F:\\javalearning\\classes",
"-classpath", "F:\\javalearning\\classes;F:\\javalearning\\lib\\fastjson-1.2.58.jar");
// 生成编译任务
JavaCompiler.CompilationTask task = compiler.getTask(null,
fileManager,
null,
options,
null,
compilationUnits);
// 执行编译任务
Boolean success = task.call();
if (success) {
System.out.println("编译成功");
} else {
System.out.println("编译失败");
}
}
编译内存中的源码需要自定义JavaFileObject
以及JavaFileManager
。下面的例子中实现了读取内存中的java源码,并将编译后的结果字节码保存在map中。
如果只需要读取内存中的源码,将**(1)**修改为上一节中对应的代码即可。
如果只需要将编译后的字节码保存在内存中,将**(2)**修改为上一节对应的代码即可。
注: (1)(2)都有两处需要修改。
public static void main(String[] args) {
// 获取java编译器
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
// 获取文件管理器
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
String className = "com.feb13th.HelloWorld";
String code = "package com.feb13th;" +
"public class HelloWorld { " +
" public static void main(String[] args) { " +
" System.out.println(\"HelloWorld!!\");" +
" }" +
"}";
// (1) 自定义JavaFileObject实现读取内存中的源文件
JavaFileObject javaFileObject = new SimpleJavaFileObject(
// 创建 uri, com/feb13th/HelloWorld.java
URI.create(className.replaceAll("\\.", "/") + JavaFileObject.Kind.SOURCE.extension),
JavaFileObject.Kind.SOURCE) {
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
return code;
}
};
// 自定义用于存储字节码的map
Map<String, byte[]> bytes = new HashMap<>();
// (2) 自定义JavaFileManager实现将编译后的字节码存储在内存中
JavaFileManager javaFileManager = new ForwardingJavaFileManager<JavaFileManager>(fileManager) {
@Override
public JavaFileObject getJavaFileForOutput(Location location,
String className,
JavaFileObject.Kind kind,
FileObject sibling) throws IOException {
if (kind == JavaFileObject.Kind.CLASS) {
return new SimpleJavaFileObject(
URI.create(className + JavaFileObject.Kind.CLASS.extension),
JavaFileObject.Kind.CLASS) {
@Override
public OutputStream openOutputStream() throws IOException {
return new FilterOutputStream(new ByteArrayOutputStream()) {
@Override
public void close() throws IOException {
out.close();
ByteArrayOutputStream bos = (ByteArrayOutputStream) out;
bytes.put(className, bos.toByteArray());
}
};
}
};
}
return super.getJavaFileForOutput(location, className, kind, sibling);
}
};
// 生成编译任务
JavaCompiler.CompilationTask task = compiler.getTask(null,
javaFileManager, //(2)
null,
null, //(1)
null,
Arrays.asList(javaFileObject));
// 执行编译任务
Boolean success = task.call();
if (success) {
System.out.println("编译成功");
} else {
System.out.println("编译失败");
}
// 查看map中有没有存储字节码
bytes.forEach((k, v) -> {
System.out.println(k);
System.out.println(v.length);
});
}