繁体   English   中英

从 zip 文件子文件夹中的 jar 文件加载类

[英]Load classes from jar file in sub-folder in zip file

我有一个具有以下结构的 zip 文件

plugin
   data
      abc.jar
      xyz.jar

我想使用这些 jars 创建一个 URLClassLoder 实例。 一种方法是对该文件进行 uzip,然后使用该文件系统路径来创建 URLClassLoaders。 我可以在内存中做同样的事情吗?

好的,您可以检查此答案https://stackoverflow.com/a/2614154/90313

和这个库http://truezip.java.net/

您可以一起使用它们来实现所需的功能。

以下是工作解决方案。 您可以根据您的要求进行修改。

zip解压参考: 在内存中递归解压缩存档

类加载参考: Java:如何将存储为 byte[] 的类加载到 JVM 中?

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

public final class ZippedJarClassReader {

    private final static String JAR_EXTENSION = ".jar";

    public static Map<String, byte[]> loadZip(final File file) throws IOException {
        final byte[]          bytes = Files.readAllBytes(file.toPath());
        final ByteArrayOutputStream baos  = new ByteArrayOutputStream();
        baos.write(bytes);
        return unzip(baos);
    }

    public static final Map<String, byte[]> unzip(final ByteArrayOutputStream baos) {
        final Map<String, byte[]> result = new HashMap<String, byte[]>();
        try(final ZipInputStream in = new ZipInputStream(new ByteArrayInputStream(baos.toByteArray()))) {
            ZipEntry       entry;
            while ((entry = in.getNextEntry()) != null) {
                final ByteArrayOutputStream os = new ByteArrayOutputStream();
                if (!entry.isDirectory()) {
                    os.write(in.readAllBytes());
                    if (entry.getName().toLowerCase().endsWith(JAR_EXTENSION)) {
                        result.putAll(unzip(os));
                    } else if (entry.getName().toLowerCase().endsWith(".class")) {
                        result.put(entry.getName().replaceAll("/", ".").substring(0, entry.getName().length() - 6), os.toByteArray());
                    }
                }
            }
            return result;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Map;

public final class ZipClassLoader extends URLClassLoader {

    private final Map<String, byte[]> classes;

    public ZipClassLoader(URL[] urls, final File zipFile) throws IOException {
        super(urls);
        classes = ZippedJarClassReader.loadZip(zipFile);
    }

    @Override
    public final Class<?> findClass(String name) throws ClassNotFoundException {
        final byte[] bytes = classes.get(name);
        if(bytes != null) return defineClass(name, bytes, 0, bytes.length);
        return super.findClass(name);
    }
}

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.zip.ZipException;

public class Test {

    @SuppressWarnings("resource")
    public static void main(String[] args) throws ZipException, IOException, ClassNotFoundException {
        final File zipFile = new File("zipfile.zip");
        final URLClassLoader loader = new ZipClassLoader(new URL[] {}, zipFile);
        final Class<?> classz = loader.loadClass("org.util.nanolog.Test");
        System.out.println(classz);
    }
}

好的, jar: URI 方案似乎不起作用,但是 nio 可以。 这是加载 Jetifier(Android 构建工具)而不解压缩所有内容的示例。

import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.FileSystem;
import java.security.SecureClassLoader;

import static java.nio.file.FileSystems.newFileSystem;
import static java.nio.file.Files.list;
import static java.nio.file.Files.readAllBytes;
import static java.nio.file.Paths.get;

class Scratch {
public static void main(String[] args) throws Exception {
    String applicationZip = "/home/mike/Downloads/jetifier-standalone.zip";
    String pathToJars = "/jetifier-standalone/lib/";
    String mainClassName = "com.android.tools.build.jetifier.standalone.Main";

    FileSystem zip = newFileSystem(get(applicationZip));
    FileSystem[] jars = list(zip.getPath(pathToJars))
            .map(jar -> {
                try { return newFileSystem(jar); }
                catch (IOException e) { throw new UncheckedIOException(e); }
            })
            .toArray(FileSystem[]::new);

    new SecureClassLoader(Scratch.class.getClassLoader()) {
        @Override protected Class<?> findClass(String name)
                throws ClassNotFoundException {
            String fileName = '/' + name.replace('.', '/') + ".class";
            for (FileSystem jar : jars) {
                try {
                    byte[] bytes = readAllBytes(jar.getPath(fileName));
                    return defineClass(name, bytes, 0, bytes.length);
                } catch (IOException ignored) {
                }
            }
            throw new ClassNotFoundException(name);
        }
    }.loadClass(mainClassName)
            .getMethod("main", String[].class)
            .invoke(null, new Object[] { new String[0] });
}
}

这有效并成功地从 Jetifier 打招呼。 我使用 JDK 13。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM