Java中在内存中编译类并将其存储到数据库中

问题描述 投票:0回答:1

我想在内存中编译一个类(例如Test1)并将字节码作为BLOB直接存储在数据库中。

此外,我还想编译一个新类(Test2),它调用存储在数据库中的Test1。所以我需要从数据库加载这个类来编译Test2。这可能吗?如果是的话我该怎么做?

我已经阅读了有关 InMemoryCompiler 和 DatabaseClassLoader 的内容,但我不确定这是否是正确的选择。

https://github.com/trung/InMemoryJavaCompiler

如何从数据库加载java类?

如果有人知道如何解决第一个问题,那就太好了。

谢谢您的回答。

java compiler-construction db2 blob in-memory
1个回答
0
投票

这可以使用

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");
    }
}

参见 https://gitlab.com/jcsahnwaldt/simple-java-tools

© www.soinside.com 2019 - 2024. All rights reserved.