<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.8</version>
</dependency>
public class DynamicClassLoader extends URLClassLoader {
public DynamicClassLoader(ClassLoader parent) {
super(new URL[0], parent);
}
public static String getClassFullName(JavaClassObject javaClassObject) {
String name = javaClassObject.getName();
name = name.substring(1, name.length() - 6);
name = name.replace("/", ".");
return name;
}
public Class<?> defineClass(JavaClassObject javaClassObject) {
String name = getClassFullName(javaClassObject);
byte[] classData = javaClassObject.getBytes();
return super.defineClass(name, classData, 0, classData.length);
}
}
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 {
/**
* Byte code created by the compiler will be stored in this
* ByteArrayOutputStream so that we can later get the byte array out of it
* and put it in the memory as an instance of our class.
*/
protected final ByteArrayOutputStream bos = new ByteArrayOutputStream();
/**
* Registers the compiled class object under URI containing the class full
* name
*
* @param name
* Full name of the compiled class
* @param kind
* Kind of the data. It will be CLASS in our case
*/
public JavaClassObject(String name, Kind kind) {
super(URI.create("string:///" + name.replace('.', '/') + kind.extension), kind);
}
/**
* Will be used by our file manager to get the byte code that can be put
* into memory to instantiate our class
*
* @return compiled byte code
*/
public byte[] getBytes() {
return bos.toByteArray();
}
/**
* Will provide the compiler with an output stream that leads to our byte
* array. This way the compiler will write everything into the byte array
* that we will instantiate later
*/
@Override
public OutputStream openOutputStream() throws IOException {
return bos;
}
}
import org.apache.commons.io.IOUtils;
import javax.tools.SimpleJavaFileObject;
import java.io.LineNumberReader;
import java.io.StringReader;
import java.net.URI;
public class CharSequenceJavaFileObject extends SimpleJavaFileObject {
/**
* CharSequence representing the source code to be compiled
*/
private String content;
/**
* This constructor will store the source code in the internal "content"
* variable and register it as a source code, using a URI containing the
* class full name
*
* @param className
* name of the public class in the source code
* @param code
* source code to compile
*/
public CharSequenceJavaFileObject(String className, String code) {
super(URI.create("string:///" + className.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);
this.content = code;
}
/**
* Answers the CharSequence to be compiled. It will give the source code
* stored in variable "content"
*/
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return content;
}
/**
* 获取某个位置的代码
*/
public String getLineCode(long line) {
LineNumberReader reader = new LineNumberReader(new StringReader(content));
int num = 0;
String codeLine = null;
try {
while ((codeLine = reader.readLine()) != null) {
num++;
if (num == line) {
break;
}
}
} catch (Throwable ignored) {
} finally {
IOUtils.closeQuietly(reader);
}
return codeLine;
}
}
import javax.tools.FileObject;
import javax.tools.ForwardingJavaFileManager;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class ClassFileManager extends ForwardingJavaFileManager<JavaFileManager> {
private JavaClassObject mainObject;
private List<JavaClassObject> innerObjects = new ArrayList<JavaClassObject>();
protected ClassFileManager(JavaFileManager fileManager) {
super(fileManager);
}
public byte[] getJavaClass() {
return mainObject.getBytes();
}
public JavaClassObject getJavaClassObject() {
return mainObject;
}
@Override
public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind,
FileObject sibling) throws IOException {
// 最后一个是mainObject
mainObject = new JavaClassObject(className, kind);
innerObjects.add(mainObject);
return mainObject;
}
public List<JavaClassObject> getInnerClassJavaClassObject() {
if (this.innerObjects != null && this.innerObjects.size() > 0) {
int size = this.innerObjects.size();
if (size == 1) {
return Collections.emptyList();
}
return this.innerObjects.subList(0, size - 1);
}
return Collections.emptyList();
}
@Override
public void close() throws IOException {
if (null != mainObject) {
mainObject.delete();
}
super.close();
}
}
import org.apache.commons.lang3.StringUtils;
import javax.tools.*;
import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class DynamicJdkCompiler {
private static final Pattern PACKAGE_PATTERN = Pattern.compile("package\\s+([_a-zA-Z][_a-zA-Z0-9\\.]*);");
private static final Pattern CLASS_PATTERN = Pattern.compile("class\\s+([_a-zA-Z][_a-zA-Z0-9]*)\\s+");
private final JavaCompiler compiler;
private URLClassLoader parentClassLoader;
private String classpath;
protected String encoding = "UTF-8";
public DynamicJdkCompiler() {
compiler = ToolProvider.getSystemJavaCompiler();
if (null == compiler) {
throw new IllegalStateException("无法获取编译器!请用JDK,而不是JRE运行JAVA。");
}
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);
}
this.classpath = sb.toString();
}
public Class<?> compile(String code) {
Matcher matcher = PACKAGE_PATTERN.matcher(code);
String packageName = null;
if (matcher.find()) {
packageName = matcher.group(1);
}
matcher = CLASS_PATTERN.matcher(code);
String className;
if (matcher.find()) {
className = matcher.group(1);
} else {
throw new IllegalArgumentException("No such class name in " + code);
}
String fullClassName = null;
if (StringUtils.isNotBlank(packageName)) {
fullClassName = packageName + "." + className;
} else {
fullClassName = className;
}
return doCompile(fullClassName, code);
}
public Class<?> doCompile(String fullClassName, String javaCode) {
DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
StandardJavaFileManager standardJavaFileManager = compiler.getStandardFileManager(diagnostics, null, null);
ClassFileManager fileManager = new ClassFileManager(standardJavaFileManager);
CharSequenceJavaFileObject jfile = new CharSequenceJavaFileObject(fullClassName, javaCode);
try {
List<JavaFileObject> jfiles = new ArrayList<JavaFileObject>();
jfiles.add(jfile);
List<String> options = new ArrayList<String>();
options.add("-encoding");
options.add(encoding);
options.add("-classpath");
options.add(this.classpath);
JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagnostics, options, null, jfiles);
boolean success = task.call();
for (Diagnostic<? extends JavaFileObject> diagnostic : diagnostics.getDiagnostics()) {
if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
String errorCode = "compile fail: " + diagnostic.getMessage(null) + ". \r\nat " + fullClassName
+ "(" + diagnostic.getLineNumber() + ")" + "\r\ncode:"
+ jfile.getLineCode(diagnostic.getLineNumber());
throw new java.lang.Error(errorCode);
}
}
if (!success) {
String error = compilePrint(diagnostics);
// System.err.print(error);
throw new IllegalStateException("Compilation failed. class: " + fullClassName + ", diagnostics: \r\n"
+ error);
}
DynamicClassLoader dynamicClassLoader = new DynamicClassLoader(this.parentClassLoader);
for (JavaClassObject javaClassObject : fileManager.getInnerClassJavaClassObject()) {
dynamicClassLoader.defineClass(javaClassObject);
}
JavaClassObject jco = fileManager.getJavaClassObject();
Class<?> clazz = dynamicClassLoader.defineClass(jco);
try {
dynamicClassLoader.close();
} catch (Exception e) {
// log.error("dynamicClassLoader.close throws " + e.getClass().getSimpleName() + " !", e);
}
return clazz;
} finally {
try {
jfile.delete();
} catch (Exception e) {
//log.error("jfile.delete throws " + e.getClass().getSimpleName() + " !", e);
}
try {
fileManager.close();
} catch (Exception e) {
//log.error("fileManager.close throws " + e.getClass().getSimpleName() + " !", e);
}
}
}
protected String compilePrint(DiagnosticCollector<JavaFileObject> diagnostics) {
StringBuilder result = new StringBuilder();
for (Diagnostic<?> diagnostic : diagnostics.getDiagnostics()) {
compilePrint(diagnostic, result);
result.append("\r\n");
}
return result.toString();
}
private void compilePrint(Diagnostic<?> diagnostic, StringBuilder stringBuilder) {
stringBuilder.append("Code:[" + diagnostic.getCode() + "]\n");
stringBuilder.append("Kind:[" + diagnostic.getKind() + "]\n");
stringBuilder.append("Position:[" + diagnostic.getPosition() + "]\n");
stringBuilder.append("Start Position:[" + diagnostic.getStartPosition() + "]\n");
stringBuilder.append("End Position:[" + diagnostic.getEndPosition() + "]\n");
stringBuilder.append("Source:[" + diagnostic.getSource() + "]\n");
stringBuilder.append("Message:[" + diagnostic.getMessage(null) + "]\n");
stringBuilder.append("LineNumber:[" + diagnostic.getLineNumber() + "]\n");
stringBuilder.append("ColumnNumber:[" + diagnostic.getColumnNumber() + "]\n");
}
}
测试类
public class TestJava {
public String test(String param){
System.out.println(param);
return param;
}
}
测试方法
@Test
public void doCompile() throws Exception {
DynamicJdkCompiler dynamicJdkCompiler = new DynamicJdkCompiler();
String java = "public class TestJava {\n" + "\n" + "\tpublic String test(String param){\n" + "\t\tSystem.out" +
".println(param);\n" + "\t\treturn param;\n" + "\t}\n" + "\n" + "}";
Class<?> compile = dynamicJdkCompiler.compile(java);
Object obj = compile.newInstance();
Method m = compile.getMethod("test", String.class);
Object invoke = m.invoke(obj, "123456");
System.out.println(invoke.toString());
}
![H@ITZ2406GLS$`G95@61(G.png