使用URLClassLoader加载jar时如何获取资源文件?

时间:2014-04-16 13:46:28

标签: java jar

我编写了一个应用程序来管理几个以jar形式提供的插件。我使用URLClassLoader加载插件类,它按预期工作。

但是现在我正在编写一个插件,它会加载一些存储在jar中的资源。如果我将此插件作为独立应用程序启动,一切正常,但如果我从应用程序内部启动它,当我尝试打开资源NullPointerException时,我会得到InputStream

我打开这样的流:

this.getClass().getResourceAsStream("/templates/template.html");

我的Eclipse项目结构如下:

src
|
+ My source files
resources
|
+ templates
  |
  + template.html

以下加载我的插件:

private List<Class<?>> loadClasses(final File[] jars) {
    List<Class<?>> classes = new ArrayList<Class<?>>();
    URL[] urls = getJarURLs(jars);
    URLClassLoader loader = new URLClassLoader(urls);

    for (File jar : jars) {
        JarFile jarFile = null;

        try {
            jarFile = new JarFile(jar);
        } catch (IOException e) {
            // Skip this jar if it can not be opened
            continue;
        }

        Enumeration<JarEntry> entries = jarFile.entries();

        while (entries.hasMoreElements()) {
            JarEntry entry = entries.nextElement();

            if (isClassFile(entry.getName())) {
                String className = entry.getName().replace("/", ".").replace(".class", "");
                Class<?> cls = null;

                try {
                    cls = loader.loadClass(className);
                } catch (ClassNotFoundException e) {
                    // Skip this jar if a class inside it can not be loaded
                    continue;
                }

                classes.add(cls);
            }
        }

        try {
            jarFile.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    try {
        loader.close();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    return classes;
}

/**
 * Checks if a path points to a class file or not.
 * 
 * @param path the filename to check
 * @return {@code true} if the path points to a class file or {@code false}
 *         if not
 */
private boolean isClassFile(final String path) {
    return path.toLowerCase().endsWith(".class") && !path.toLowerCase().contains("package-info");
}

然后我使用newInstance()从这些类中创建实例。

我认为插件jar的根路径与应用程序的根路径不同,或者没有加载jar文件的所有内容,或者两者都是......

有人可以帮助我吗?

1 个答案:

答案 0 :(得分:1)

首先请注意,使用getClass().getResource(...)也会委托ClassLoaderURLClassLoader也负责加载资源。使用哪个类加载器?它与加载类的类加载器相同。点。

在您的代码中,您构建了一个URLClassLoader来加载某些类。因此,如果上面提到的调用来自插件中的类,则相同的loadClass将用于加载资源。

这一切似乎都没问题......但你犯了一点错误。在加载结束时,您还关闭了加载程序。这样可以防止后续调用getResourceURLClassLoader返回任何有意义的内容。实际上,它可能为null,因为现在加载器无法再加载资源了。

结论:如果您仍需要加载目的,请不要关闭{{1}}。而是保持对此类加载器的引用,并在程序运行时结束时将其关闭。