Loading a JAR dynamically from memory

邮差的信 提交于 2020-01-21 04:51:25

问题


I want to load a JAR dynamically, straight for memory.
Say, I have a buffer that contains a JAR, and I want to load all the classes inside the JAR, or at least list all of the files that exist inside the JAR. (classes, images, etc...).
What do I do if the first class that I load depends on the second class, for example? Does java know how to handle this? Or I have take care of this by myself?


回答1:


Since you said “at least list all of the files that exist inside the JAR”, let’s begin with that rather easy task.

Suppose, you have your JarFile in a byte array, byte[] buffer:

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

Loading classes from such a representation doesn’t work out-of-the-box because the standard ClassLoader implementations rely on the JarFile implementation which relies on a physical file rather than an abstraction.

So unless you simply write the buffer into a temporary file, it boils down to implement your own ClassLoader. Since the JRE supports only stream access as shown above, you will have to scan linearly to find a requested resource/class or iterate once and store the entries into a Map.

One alternative to implementing a ClassLoader is to implement a custom URL handler to use together with a URLClassLoader which reduces the task to the lookup as described above:

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



回答2:


You may have to write the jar to the disk first, then you can use the following to add it to the classpath: (full answer here)

URLClassLoader child = new URLClassLoader (myJar.toURL(), this.getClass().getClassLoader());
Class classToLoad = Class.forName ("com.MyClass", true, child);
Method method = classToLoad.getDeclaredMethod ("myMethod");
Object instance = classToLoad.newInstance ();
Object result = method.invoke (instance);

If you want to enumerate the content of a jar that is not in the classpath, you can always treat it as a zip file: (see full answer here)

ZipFile zipFile = new ZipFile("testfile.zip");
Enumeration zipEntries = zipFile.entries();
String fname;
while (zipEntries.hasMoreElements()) {
    fname = ((ZipEntry)zipEntries.nextElement()).getName();
}



回答3:


You should use custom ClassLoader and set the JAR file to its classpath.

Classes are always loaded lazily, you do not explicitly load them. Once the JAR is on classpath of the ClassLoader, you can resolve resources.




回答4:


Here you can use URLClassloader to load a jar. see in the example I have a jar that has class with name com.x.Test and it has print method, Below code is describ how to load class and invoke the method, It just an example but better use of Interfaces, factory methods and reflection can make this code better,

File jarFile = new File("myspace\\my.jar");
URLClassLoader loader = new URLClassLoader(new URL[]{jarFile.toURL()});
Class<?> clazz = loader.loadClass("com.x.Test");
clazz.getMethod("print").invoke(clazz.getConstructor().newInstance(), args);


来源:https://stackoverflow.com/questions/28964450/loading-a-jar-dynamically-from-memory

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!