在产品中有时会遇到想动态调用java代码的时候,即在运行过程中,调用一段java代码字符串,经调研是可行的。代码如下:
import javax.tools.*;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static java.nio.file.Files.readAllBytes;
import static java.nio.file.Paths.get;
public class InlineCompiler {
public static void main(String[] args) {
String filename = "code.txt";
String code = "";
try {
code = new String(readAllBytes(get(filename)));
} catch (Exception e) {
}
System.out.println(code);
String klassName = "GetMachineNo";
String methodName = "execute";
Map<String, Object> params = new HashMap<String, Object>();
params.put("machine_no", "a1b2");
params.put("abc", "def");
String result = getResult(code, klassName, methodName, params);
System.out.println("result: " + result);
}
public static String getResult(String code, String klassName, String methodName, Map<String, Object> params) {
try {
DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
//System.out.println("compiler: " + compiler);
//StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null);
ClassFileManager fileManager = new ClassFileManager(compiler.getStandardFileManager(diagnostics, null, null));
List<String> optionList = new ArrayList<String>();
//Iterable<? extends JavaFileObject> compilationUnit = fileManager.getJavaFileObjectsFromFiles(Arrays.asList(codeFile));
List<JavaFileObject> jfiles = new ArrayList<>();
jfiles.add(new CharSequenceJavaFileObject(klassName, code));
JavaCompiler.CompilationTask task = compiler.getTask(
null,
fileManager,
diagnostics,
optionList,
null,
//compilationUnit);
jfiles);
if (task.call()) {
//URLClassLoader classLoader = new URLClassLoader(new URL[]{new File("/tmp/javacode").toURI().toURL()});
//Class<?> loadedClass = classLoader.loadClass(klassName);
JavaClassObject jco = fileManager.getJavaClassObject();
//DynamicClassLoader dynamicClassLoader = new DynamicClassLoader(this.parentClassLoader);
DynamicClassLoader dynamicClassLoader = new DynamicClassLoader(Thread.currentThread().getContextClassLoader());
Class<?> loadedClass = dynamicClassLoader.loadClass(klassName, jco);
Object obj = loadedClass.newInstance();
Method[] methods = loadedClass.getDeclaredMethods();
Method callMethod = null;
for (Method method : methods) {
if (method.getName().equals(methodName)) {
System.out.println("mn: " + method.getName());
callMethod = method;
break;
}
}
callMethod.setAccessible(true);
String result = (String) callMethod.invoke(obj, params);
return result;
} else {
for (Diagnostic<? extends JavaFileObject> diagnostic : diagnostics.getDiagnostics()) {
System.out.format("Error on line %d in %s%n",
diagnostic.getLineNumber(),
diagnostic.getSource().toUri());
}
}
fileManager.close();
} catch (IOException | InstantiationException | IllegalAccessException | InvocationTargetException exp) {
exp.printStackTrace();
}
return "";
}
}
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;
}
}
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;
}
}
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();
}
@Override
public OutputStream openOutputStream() throws IOException {
return outputStream;
}
}
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);
}
}
执行结果:
import java.util.Map;
public class GetMachineNo {
public String execute(Map<String, Object> map) {
try {
return String.valueOf(map.get("machine_no")) + "_" + String.valueOf(map.get("abc"));
} catch (Exception e) {
}
return "";
}
}
mn: execute
result: a1b2_def