【问题标题】:Using getResourceAsStream on jar loaded by Class.forName(...)在 Class.forName(...) 加载的 jar 上使用 getResourceAsStream
【发布时间】:2013-06-28 16:03:15
【问题描述】:

现在,在我开始之前 - 我是 Java 新手,我有点陷入了困境。

我有一个系统,我可以将类从 jar 文件以插件的形式加载到我的应用程序中:

URLClassLoader loader = new URLClassLoader(urlList);
// urlList is a 1 slice URL[] with a single URL to a jar file in it
Class<?> toolClass = Class.forName(className, true, loader);
// className is a String with the main class name in the jar

这一切都很好——我可以在加载的类中调用方法,一切正常。

现在,我希望能够在该 jar 文件中存储各种资源并让类能够访问它们。我一直在看 getResourceAsStream(),看起来它应该是要走的路:

InputStream in = this.getClass().getClassLoader().getResourceAsStream("path/to/my/test.txt");

... 除了它总是返回 null。我一直在关注有关此功能的示例和其他论坛帖子等,但似乎没有一个与我的具体情况有关:

  1. jar 文件在运行时使用 URLClassLoader 加载。
  2. jar 文件不在 classPath 中。

那么我在这些情况下尝试做的事情实际上可能吗,还是我必须求助于手动解压缩 jar 文件来获取资源? (老实说,我不想那样做。)


编辑:以下是我正在使用的功能:

加载一个jar文件:

public void loadPlugin(File jar)
{
    try {
        URL[] urlList = new URL[1];
        urlList[0]  = jar.toURI().toURL();

        URLClassLoader loader = new URLClassLoader(urlList);

        String className = null;
        className = findClassInZipFile(jar); // This just walks the zip file looking for the class

        if (className == null) {
            return;
        }

        JarFile jf = new JarFile(jar);
        Manifest manifest = jf.getManifest();
        Attributes manifestContents = manifest.getMainAttributes();

        Map pluginInfo = new LinkedHashMap();
        pluginInfo.put("version", manifestContents.getValue("Version"));
        pluginInfo.put("compiled", manifestContents.getValue("Compiled"));
        pluginInfo.put("jarfile", jar.getAbsolutePath());

        Class<?> toolClass;
        try {
            toolClass = Class.forName(className, true, loader);
        } catch (Exception ex) {
            ex.printStackTrace();
            return;
        }
        Tool tool = (Tool) toolClass.newInstance();

        // If the setInfo method doesn't exist we don't care.
        try {
            tool.setInfo(pluginInfo);
        } catch (Exception blah) {
        }
        plugins.put(className, tool);
    } catch (Exception e) {
        System.err.println(e.getMessage());
    }
}

以及结果类中的函数:

public void run() {
    try {
        InputStream in = this.getClass().getClassLoader().getResourceAsStream("uecide/app/tools/test.txt");
        if (in == null) {
            System.err.println("FAIL!!!");
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

jar 文件看起来像:

   0 Fri Jun 28 16:52:32 BST 2013 META-INF/
 140 Fri Jun 28 16:52:30 BST 2013 META-INF/MANIFEST.MF
   0 Fri Jun 28 16:52:32 BST 2013 uecide/
   0 Fri Jun 28 16:52:32 BST 2013 uecide/app/
   0 Fri Jun 28 16:52:32 BST 2013 uecide/app/tools/
1880 Fri Jun 28 16:52:32 BST 2013 uecide/app/tools/ExportToMPLABX.class
2754 Fri Jun 28 16:52:32 BST 2013 uecide/app/tools/test.txt

【问题讨论】:

  • 应该没问题。我怀疑您提供了错误的资源名称。例如,资源名称区分大小写 - 您是否仔细检查了您提供的确切名称?
  • 资源路径必须以/!您的示例中没有/,这可能是您的问题
  • @fge:这是不正确的。如果使用Class.getResourceAsStream() 从根目录而不是类包开始,则它们必须以/ 开头。如果使用ClassLoader.getResourceAsStream(),则不能以/开头。
  • 您的示例代码中的this 是什么?它是与创建 URLClassLoader 的对象相同的对象,还是从 jar 加载的类的实例?
  • @Majenko:您是否仔细检查过this.getClass().getClassLoader() 正在返回您期望的类加载器?它可能会委托给 Class.forName 中的另一个...

标签: java class jar


【解决方案1】:

正如我们所发现的,问题在于新的类加载器首先委托给父类加载器,父类加载器正在寻找类。

选项:

  • 确保当前的类加载器没有有你要加载的类
  • 在构造新的URLClassLoader 时指定父类加载器引用(可能是null?)

【讨论】:

  • 对 URLClassLoader 的父类加载器使用 null 会导致它无法找到类。
  • @Majenko:这很有趣——这表明它查找 jar 文件的方式有问题。嗯 - 我怀疑值得调试。
  • 我想我会坚持在加载时将加载器传递给类。我认为我没有足够的脑力来解决这个问题。
猜你喜欢
  • 1970-01-01
  • 2014-12-24
  • 2013-11-16
  • 1970-01-01
  • 2013-01-24
  • 1970-01-01
  • 2011-05-16
  • 1970-01-01
  • 2023-04-03
相关资源
最近更新 更多