我想在内存中编译一个类(例如Test1)并将字节码作为BLOB直接存储在数据库中。
此外,我还想编译一个新类(Test2),它调用存储在数据库中的Test1。所以我需要从数据库加载这个类来编译Test2。这可能吗?如果是的话我该怎么做?
我已经阅读了有关 InMemoryCompiler 和 DatabaseClassLoader 的内容,但我不确定这是否是正确的选择。
https://github.com/trung/InMemoryJavaCompiler
如果有人知道如何解决第一个问题,那就太好了。
谢谢您的回答。
这可以使用
javax.tools.JavaCompiler
和相关类来完成。
但这有点棘手,因为
javax.tools
没有很好的记录。通过反复试验,我发现我们需要一个特殊的 javax.tools.JavaFileManager
来覆盖 list()
和 inferBinaryName()
。
我只是将已编译类的字节码存储在哈希映射中,但是将该部分更改为数据库应该不会太困难。
package simple.tools;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import javax.tools.Diagnostic;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.JavaFileObject;
import javax.tools.JavaFileObject.Kind;
import javax.tools.SimpleJavaFileObject;
import javax.tools.ToolProvider;
import javax.tools.FileObject;
import javax.tools.ForwardingJavaFileManager;
import javax.tools.JavaFileManager;
class NamedJavaFileObject extends SimpleJavaFileObject {
final String name;
protected NamedJavaFileObject(String name, Kind kind) {
super(URI.create(name.replace('.', '/') + kind.extension), kind);
this.name = name;
}
}
public class SimpleCompiler {
public static JavaFileObject sourceFile(String name, String source) {
return inputFile(name, Kind.SOURCE, source);
}
private static JavaFileObject inputFile(String name, Kind kind, String content) {
return new NamedJavaFileObject(name, kind) {
@Override
public CharSequence getCharContent(boolean b) {
return content;
}
};
}
private static JavaFileObject inputFile(String name, Kind kind, byte[] content) {
return new NamedJavaFileObject(name, kind) {
@Override
public InputStream openInputStream() {
return new ByteArrayInputStream(content);
}
};
}
private JavaFileObject outputFile(String name, Kind kind) {
return new NamedJavaFileObject(name, kind) {
@Override
public OutputStream openOutputStream() {
return outputStream(name);
}
};
}
private OutputStream outputStream(String name) {
return new ByteArrayOutputStream() {
@Override
public void close() {
storeClass(name, toByteArray());
}
};
}
private void storeClass(String name, byte[] bytes) {
classes.put(name, bytes);
JavaFileObject file = inputFile(name, Kind.CLASS, bytes);
int dot = name.lastIndexOf('.');
String pkg = dot == -1 ? "" : name.substring(0, dot);
packages.computeIfAbsent(pkg, k -> new ArrayList<>()).add(file);
}
private final Map<String, byte[]> classes = new HashMap<>();
private final Map<String, List<JavaFileObject>> packages = new HashMap<>();
private final ClassLoader loader = new ClassLoader() {
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] bytes = classes.get(name);
if (bytes == null) throw new ClassNotFoundException(name);
return super.defineClass(name, bytes, 0, bytes.length);
}
};
private final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
private final JavaFileManager manager =
new ForwardingJavaFileManager<JavaFileManager>(compiler.getStandardFileManager(null, null, null)) {
@Override
public JavaFileObject getJavaFileForOutput(Location loc, String name, Kind kind, FileObject obj) {
return outputFile(name, kind);
}
@Override
public Iterable<JavaFileObject> list(Location loc, String pkg, Set<Kind> kinds, boolean rec) throws IOException {
List<JavaFileObject> files = packages.get(pkg);
if (files != null) return files;
else return super.list(loc, pkg, kinds, rec);
}
@Override
public String inferBinaryName(Location loc, JavaFileObject file) {
if (file instanceof NamedJavaFileObject) return ((NamedJavaFileObject)file).name;
else return super.inferBinaryName(loc, file);
}
};
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loader.loadClass(name);
}
public Class<?> compile(String name, String source) throws ClassNotFoundException {
compile(sourceFile(name, source));
return loadClass(name);
}
public void compile(JavaFileObject... files) {
compile(Arrays.asList(files));
}
public void compile(List<JavaFileObject> files) {
if (files.isEmpty()) throw new RuntimeException("No input files");
DiagnosticCollector<JavaFileObject> collector = new DiagnosticCollector<>();
CompilationTask task = compiler.getTask(null, manager, collector, null, null, files);
boolean success = task.call();
check(success, collector);
}
private void check(boolean success, DiagnosticCollector<?> collector) {
for (Diagnostic<?> diagnostic : collector.getDiagnostics()) {
if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
throw new RuntimeException(diagnostic.getMessage(Locale.US));
}
}
if (! success) throw new RuntimeException("Unknown error");
}
}