本文主要实现java code 动态编译,并使用自定义的ClassLoader加载动态编译生成的字节码。
DynamicCompile 类主负责调用JDK API 实现动态编译以及使用ClassLoader加载编译后生成的字节码。
import javax.tools.*;
import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
public class DynamicCompile {
private URLClassLoader parentClassLoader;
private String classpath;
public DynamicCompile() {
this.parentClassLoader = (URLClassLoader) this.getClass().getClassLoader();
this.buildClassPath();// 存在动态安装的问题,需要动态编译类路径
}
private void buildClassPath() {
this.classpath = null;
StringBuilder sb = new StringBuilder();
for (URL url : this.parentClassLoader.getURLs()) {
String p = url.getFile();
sb.append(p).append(File.pathSeparator); //路径分割符linux为:window系统为;
}
this.classpath = sb.toString();
}
/**
* 编译出类
*
* @param fullClassName 全路径的类名
* @param javaCode java代码
*
* @return 目标类
*/
public Class> compileToClass(String fullClassName, String javaCode) throws Exception {
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
DiagnosticCollector diagnostics = new DiagnosticCollector<>();
ClassFileManager fileManager = new ClassFileManager(compiler.getStandardFileManager(diagnostics, null, null));
List jfiles = new ArrayList<>();
jfiles.add(new CharSequenceJavaFileObject(fullClassName, javaCode));
List options = new ArrayList<>();
options.add("-encoding");
options.add("UTF-8");
options.add("-classpath");
options.add(this.classpath);
JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagnostics, options, null, jfiles);
boolean success = task.call();
if (success) {
JavaClassObject jco = fileManager.getJavaClassObject();
DynamicClassLoader dynamicClassLoader = new DynamicClassLoader(this.parentClassLoader);
//加载至内存
return dynamicClassLoader.loadClass(fullClassName, jco);
} else {
for (Diagnostic diagnostic : diagnostics.getDiagnostics()) {
String error = compileError(diagnostic);
throw new RuntimeException(error);
}
throw new RuntimeException("compile error");
}
}
private String compileError(Diagnostic diagnostic) {
StringBuilder res = new StringBuilder();
res.append("LineNumber:[").append(diagnostic.getLineNumber()).append("]\n");
res.append("ColumnNumber:[").append(diagnostic.getColumnNumber()).append("]\n");
res.append("Message:[").append(diagnostic.getMessage(null)).append("]\n");
return res.toString();
}
}
CharSequenceJavaFileObject 用于包装原始的java code 。
import javax.tools.SimpleJavaFileObject;
import java.net.URI;
public class CharSequenceJavaFileObject extends SimpleJavaFileObject {
private CharSequence code;
public CharSequenceJavaFileObject(String className, CharSequence code) {
super(URI.create("string:///" + className.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);
this.code = code;
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return code;
}
}
JavaClassObject 存放编译后结果
import javax.tools.SimpleJavaFileObject;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;
public class JavaClassObject extends SimpleJavaFileObject {
private final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
public JavaClassObject(String name, Kind kind) {
super(URI.create("string:///" + name.replace('.', '/') + kind.extension), kind);
}
public byte[] getBytes() {
return outputStream.toByteArray();
}
//编译时候会调用openOutputStream获取输出流,并写数据
@Override
public OutputStream openOutputStream() throws IOException {
return outputStream;
}
}
文件管理类
import javax.tools.FileObject;
import javax.tools.ForwardingJavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import java.io.IOException;
public class ClassFileManager extends ForwardingJavaFileManager {
private JavaClassObject jclassObject;
public ClassFileManager(StandardJavaFileManager standardManager) {
super(standardManager);
}
public JavaClassObject getJavaClassObject() {
return jclassObject;
}
//需要覆盖
@Override
public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind,
FileObject sibling) throws IOException {
jclassObject = new JavaClassObject(className, kind);
return jclassObject;
}
}
classLoader 加载编译生成的字节码
public class DynamicClassLoader extends ClassLoader {
public DynamicClassLoader(ClassLoader parent) {
super(parent);
}
public Class loadClass(String fullName, JavaClassObject jco) {
byte[] classData = jco.getBytes();
return this.defineClass(fullName, classData, 0, classData.length);
}
}
public class Main {
public static void main(String[] args) throws Exception {
DynamicCompile dynamicCompile = new DynamicCompile();
String code = "package com.hsc.study.com.hsc.test;\n" +
"\n" +
"/**\n" +
" * Created by yufei on 17/5/13.\n" +
" */\n" +
"public class Test {\n" +
" @Override\n" +
" public String toString() {\n" +
" return \"test\"\n" + ";" +
" }\n" +
"}\n";
Class> classz = dynamicCompile.compileToClass("com.hsc.study.Test", code);
System.out.println(classz.newInstance());
}
}
public class Test {
@Override
public String toString() {
return "test";
}
}
输出:test