从内存动态加载JAR

Joh*_*Doe 6 java jar classloader

我想动态加载JAR,直接加载内存.
说,我有一个包含JAR的缓冲区,我想加载JAR中的所有类,或者至少列出JAR中存在的所有文件.(课程,图像等......).
如果我加载的第一个类依赖于第二个类,我该怎么办?java知道如何处理这个吗?或者我自己照顾这个?

Hol*_*ger 6

既然你说"至少列出JAR中存在的所有文件",让我们从这个相当容易的任务开始吧.

假设,你的JarFile是一个字节数组,byte[] buffer:

try(JarInputStream is=new JarInputStream(new ByteArrayInputStream(buffer))) {
    for(;;) {
        JarEntry nextEntry = is.getNextJarEntry();
        if(nextEntry==null) break;
        System.out.println(nextEntry);
    }
}
Run Code Online (Sandbox Code Playgroud)

从这样的表示中加载类不是开箱即用的,因为标准ClassLoader实现依赖于JarFile依赖于物理文件而不是抽象的实现.

因此,除非您只是将缓冲区写入临时文件,否则可以归结为实现自己的缓冲区ClassLoader.由于JRE仅支持如上所示的流访问,因此您必须线性扫描以查找请求的资源/类或迭代一次并将条目存储到a中Map.

实现a的一种替代方法ClassLoader是实现自定义URL处理程序以与如下URLClassLoader所述将任务减少到查找一起使用:

final Map<String,byte[]> map=new HashMap<>();
try(JarInputStream is=new JarInputStream(new ByteArrayInputStream(buffer))) {
    for(;;) {
        JarEntry nextEntry = is.getNextJarEntry();
        if(nextEntry==null) break;
        final int est=(int)nextEntry.getSize();
        byte[] data=new byte[est>0? est: 1024];
        int real=0;
        for(int r=is.read(data); r>0; r=is.read(data, real, data.length-real))
            if(data.length==(real+=r)) data=Arrays.copyOf(data, data.length*2);
        if(real!=data.length) data=Arrays.copyOf(data, real);
        map.put("/"+nextEntry.getName(), data);
    }
}
URL u=new URL("x-buffer", null, -1, "/", new URLStreamHandler() {
    protected URLConnection openConnection(URL u) throws IOException {
        final byte[] data = map.get(u.getFile());
        if(data==null) throw new FileNotFoundException(u.getFile());
        return new URLConnection(u) {
            public void connect() throws IOException {}
            @Override
            public InputStream getInputStream() throws IOException {
                return new ByteArrayInputStream(data);
            }
        };
    }
});
try(URLClassLoader cl=new URLClassLoader(new URL[]{u})) {
    cl.loadClass( « a class from your JarFile buffer »);
}
Run Code Online (Sandbox Code Playgroud)

  • @tporeba规范中没有要求,对于类加载器资源,必须可以将URL转换为字符串然后再转换为URL,并且超出了本问答的范围,但是如果需要,可以使用[ setURLStreamHandlerFactory`](https://docs.oracle.com/javase/8/docs/api/java/net/URL.html#setURLStreamHandlerFactory-java.net.URLStreamHandlerFactory-)注册一个工厂,该工厂返回用于以下操作的流处理程序x缓冲区网址。如果存在,您必须注意与现有的自定义处理程序工厂进行互操作。这就是为什么我没有为此答案做这个。 (2认同)

Cra*_*ing 0

您应该使用自定义ClassLoader并将 JAR 文件设置为其类路径。

类始终是延迟加载的,您无需显式加载它们。一旦 JAR 位于 的类路径上ClassLoader,您就可以解析资源。