具有资源加载的自定义ClassLoader

时间:2015-08-21 18:20:55

标签: java classloader

我正在编写一个插件加载器 - 它加载不在类路径上的jar。我写了一个简单的自定义ClassLoader,它在其构造函数中接受一个JarFile,并在JarFile中查找指定的类。这个加载器只是覆盖ClassLoader的findClass()方法,并且工作正常。

然后我确定我还需要能够从插件jar获取资源。所以我覆盖了findResource()。这有意外的结果导致基本插件类无法在jar中找到其他类:我得到NoClassDefFoundErrors!

换句话说,如果我有包含MyPlugin和MyPluginComponent的plugin.jar:

  • 如果我不覆盖findResource(),那么我可以加载jar,创建一个MyPlugin实例,然后又可以创建一个MyPluginComponent。但是我找不到捆绑在jar中的资源。
  • 如果我覆盖findResource(),那么我可以加载jar,创建MyPlugin的实例,如果MyPlugin尝试创建MyPluginComponent,我会得到NoClassDefFoundError。

这表明findResource()的实现无论如何都找不到类文件 - 但它甚至没有被调用(根据我的日志记录),所以我不知道是怎么回事。这种互动如何解决,我该如何解决?

我尝试编写一个小的自包含示例,并且遇到困难,手动生成一个不会产生“不兼容的幻数”错误的jar文件。希望无论我做错什么,单从类加载器就可以看出来。很抱歉给您带来不便,感谢您抽出宝贵时间。

import com.google.common.io.CharStreams;

import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;

import java.lang.ClassLoader;

import java.net.URL;

import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * Custom class loader for loading plugin classes. Adapted from
 * http://kalanir.blogspot.com/2010/01/how-to-write-custom-class-loader-to.html
 */
public static class PluginLoader extends ClassLoader {
    private JarFile jarFile_;
    public PluginLoader(JarFile jarFile) {
        super(Thread.currentThread().getContextClassLoader());
        jarFile_ = jarFile;
    }

    @Override
    public Class findClass(String className) {
        try {
            // Replace "." with "/" for seeking through the jar.
            String classPath = className.replace(".", "/") + ".class";
            System.out.println("Searching for " + className + " under " + classPath);
            JarEntry entry = jarFile_.getJarEntry(classPath);
            if (entry == null) {
                return null;
            }
            InputStream stream = jarFile_.getInputStream(entry);
            String contents = CharStreams.toString(
                    new InputStreamReader(stream));
            stream.close();
            byte[] bytes = contents.getBytes();
            Class result = defineClass(className, bytes, 0, bytes.length);
            return result;
        }
        catch (IOException e) {
            System.out.println(e + "Unable to load jar file " + jarFile_.getName());
        }
        catch (ClassFormatError e) {
            System.out.println(e + "Unable to read class data for class " + className + " from jar " + jarFile_.getName());
        }
        return null;
    }

    @Override
    protected URL findResource(String name) {
        System.out.println("Asked to find resource at " + name);
        try {
            String base = new File(jarFile_.getName()).toURI().toURL().toString();
            URL result = new URL(String.format("jar:%s!/%s", base, name));
            System.out.println("Result is " + result);
            return result;
        }
        catch (IOException e) {
            System.out.println(e + "Unable to construct URL to find " + name);
            return null;
        }
    }

    @Override
    public InputStream getResourceAsStream(String name) {
        System.out.println("Getting resource at "  +name);
        JarEntry entry = jarFile_.getJarEntry(name);
        if (entry == null) {
            System.out.println("Couldn't find resource " + name);
            return null;
        }
        try {
            return jarFile_.getInputStream(entry);
        }
        catch (IOException e) {
            System.out.println(e + "Unable to load resource " + name + " from jar file " + jarFile_.getName());
            return null;
        }
    }
}

1 个答案:

答案 0 :(得分:0)

如果您的要求只是阅读额外的JAR文件,那么延长public class PluginLoader extends URLClassLoader { public PluginLoader(String jar) throws MalformedURLException { super(new URL[] { new File(jar).toURI().toURL() }); } } 可能是更好的选择。

script.pl(filename)