介紹
java5之前我們可以通過java提供的tools.jar來操作java編譯器,java6提供了新的API,讓我們可以更方便的調用。包名為javax.tools。
使用
通過文件編譯
String filePath = "D:\\Client.java";
//獲取java編譯器
JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
//編譯
int result = javaCompiler.run(null, null, null, filePath);
System.out.println(result);
結果為0表示編譯成功,在相同目錄下生成了Client.class文件。
編譯參數依次為
java編譯器提供參數,如果為null,以System.in代替
得到Java編譯器的輸出信息,如果為null,以System.out代替
接收編譯器的錯誤信息,如果為null,以System.err代替
一個或多個Java源程式文件
通過非文件格式編譯
java還提供了編譯其他形式的源文件的功能,如內存字符串文本,數據庫讀取的文本。
public class JavaFileManagerMain {
public static void main(String[] args) {
//文件路徑
String fullQuanlifiedFileName = "D:\\Client.java";
//獲取編譯器
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
//獲取文件管理器 參數依次為錯誤監聽器,區域對象,編碼
StandardJavaFileManager fileManager =
compiler.getStandardFileManager(null, null, null);
//通過文件全路徑獲取要編譯的文件對象
Iterable extends JavaFileObject> files =
fileManager.getJavaFileObjectsFromStrings(
Arrays.asList(fullQuanlifiedFileName));
//創建編譯任務 參數為錯誤輸出流,文件管理器,錯誤處理器,編譯器選項,參與編譯的class,帶編譯的java文件
JavaCompiler.CompilationTask task = compiler.getTask(
null, fileManager, null, null, null, files);
//執行任務
Boolean result = task.call();
if (result) {
System.out.println("Succeeded");
}
}
}
接下來實現從內存中讀取待編譯對象
public class StringObject extends SimpleJavaFileObject {
private String content = null;
protected StringObject(String className, String contents) throws URISyntaxException {
super(new URI(className), Kind.SOURCE);
this.content = contents;
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return content;
}
}
public class StringClassCompilerMain {
public static void main(String[] args) {
JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager standardJavaFileManager = javaCompiler.getStandardFileManager(null, null, null);
JavaFileObject testFile = generateTest();
Iterable extends JavaFileObject> classes = Arrays.asList(testFile);
JavaCompiler.CompilationTask task = javaCompiler.getTask(null, standardJavaFileManager, null, null, null, classes);
if (task.call()) {
System.out.println("success");
} else {
System.out.println("failure!");
}
}
//通過字符串創建一個待編譯對象
private static JavaFileObject generateTest() {
String contents = "package com.imooc.sourcecode.java.javacompile.test3;" +
"class Test {\n" +
" public static void main(String[] args) {\n" +
" System.out.println(\"success\");\n" +
" }\n" +
"}\n";
StringObject so = null;
try {
so = new StringObject("com.imooc.sourcecode.java.javacompile.test3.Test", contents);
} catch (URISyntaxException e) {
e.printStackTrace();
}
return so;
}
}
結果編譯成功。
實現在運行期編譯及加載類
定義源代碼存儲類
/**
* 待編譯對象 存儲待編譯的字符串
*/
public class JavaSourceFileObject extends SimpleJavaFileObject {
//表示java源代碼
private CharSequence content;
protected JavaSourceFileObject(String className, String content) {
super(URI.create("string:///" + className.replaceAll("\\.", "/") + Kind.SOURCE.extension), Kind.SOURCE);
this.content = content;
}
/**
* 獲取需要編譯的源代碼
*
* @param ignoreEncodingErrors
* @return
* @throws IOException
*/
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
return content;
}
}
定義編譯結果存儲類
/**
* 存儲編譯之后的class內容
*/
public class JavaTargetFileObject extends SimpleJavaFileObject {
/**
* Compiler編譯后的byte數據會存在這個ByteArrayOutputStream對象中,
* 后面可以取出,加載到JVM中。
*/
private ByteArrayOutputStream byteArrayOutputStream;
public JavaTargetFileObject(String className, Kind kind) {
super(URI.create("string:///" + className.replaceAll("\\.", "/") + kind.extension), kind);
this.byteArrayOutputStream = new ByteArrayOutputStream();
}
/**
* 覆蓋父類SimpleJavaFileObject的方法。
* 該方法提供給編譯器結果輸出的OutputStream。
*
* 編譯器完成編譯后,會將編譯結果輸出到該 OutputStream 中,我們隨后需要使用它獲取編譯結果
*
* @return
* @throws IOException
*/
@Override
public OutputStream openOutputStream() throws IOException {
return this.byteArrayOutputStream;
}
/**
* FileManager會使用該方法獲取編譯后的byte,然后將類加載到JVM
*/
public byte[] getBytes() {
return this.byteArrayOutputStream.toByteArray();
}
}
定義自己的文件管理器
/**
* 內存文件管理器
* @see JavaTargetFileObject
*/
public class ClassFileManager extends ForwardingJavaFileManager {
/**
* 存儲編譯后的代碼數據
*/
private JavaTargetFileObject classJavaFileObject;
protected ClassFileManager(JavaFileManager fileManager) {
super(fileManager);
}
/**
* 編譯后加載類
*
* 返回一個匿名的SecureClassLoader:
* 加載由JavaCompiler編譯后,保存在ClassJavaFileObject中的byte數組。
*/
@Override
public ClassLoader getClassLoader(Location location) {
return new SecureClassLoader() {
@Override
protected Class> findClass(String name) throws ClassNotFoundException {
byte[] bytes = classJavaFileObject.getBytes();
return super.defineClass(name, bytes, 0, bytes.length);
}
};
}
/**
* 給編譯器提供JavaClassObject,編譯器會將編譯結果寫進去
*/
@Override
public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind, FileObject sibling)
throws IOException {
this.classJavaFileObject = new JavaTargetFileObject(className, kind);
return this.classJavaFileObject;
}
}
定義一個實現類編譯和加載
/**
* 運行時編譯
*/
public class DynamicCompiler {
private JavaFileManager fileManager;
public DynamicCompiler() {
this.fileManager = initManger();
}
private JavaFileManager initManger() {
if (fileManager != null) {
return fileManager;
} else {
JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
DiagnosticCollector diagnosticCollector = new DiagnosticCollector<>();
fileManager = new ClassFileManager(javaCompiler.getStandardFileManager(diagnosticCollector, null, null));
return fileManager;
}
}
/**
* 編譯源碼并加載,獲取Class對象
*
* @param fullName
* @param sourceCode
* @return
* @throws ClassNotFoundException
*/
public Class compileAndLoad(String fullName, String sourceCode) throws ClassNotFoundException {
JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
List javaFileObjectList = new ArrayList<>();
javaFileObjectList.add(new JavaSourceFileObject(fullName, sourceCode));
boolean result = javaCompiler
.getTask(null, fileManager, null, null, null, javaFileObjectList)
.call();
if (result) {
return this.fileManager.getClassLoader(null).loadClass(fullName);
} else {
return Class.forName(fullName);
}
}
/**
* 關閉fileManager
*
* @throws IOException
*/
public void close() throws IOException {
this.fileManager.close();
}
}