【问题标题】:Load classes from jar file in sub-folder in zip file从 zip 文件的子文件夹中的 jar 文件加载类
【发布时间】:2012-03-13 15:11:00
【问题描述】:

我有一个结构如下的 zip 文件

plugin
   data
      abc.jar
      xyz.jar

我想使用这些 jar 创建一个 URLClassLoder 实例。 一种方法是解压缩此文件,然后使用该文件系统路径创建 URLClassLoaders。 我可以在内存中做同样的事情吗?

【问题讨论】:

  • 取决于这些文件有多大。如果它们太大,我不会在内存中这样做。无论如何,你为什么不把它们解压并正常加载呢?
  • jar 可以在 META-INF/lib 中有库

标签: java classloader


【解决方案1】:

好的,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。

【讨论】:

    【解决方案2】:

    以下是有效的解决方案。您可以根据自己的需要进行修改。

    zip解包参考:recursively unzip archive in memory

    类加载参考:Java: How to load Class stored as byte[] into the 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);
        }
    }
    

    【讨论】:

    • @Miha_x64 这种方法有什么缺点吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-11
    • 2015-04-05
    • 1970-01-01
    相关资源
    最近更新 更多