URLClassLoader是否正确遍历MANIFEST.MF Class-Path标头?

时间:2016-10-06 21:41:01

标签: java urlclassloader

更新1:实际上,网址格式差异会导致错误。这是一个单元测试(手工切割和混淆;希望我没有错过任何东西)显示问题:

@Test
public void wheresWaldo2() throws ClassNotFoundException, IllegalAccessException, InstantiationException, IOException, NoSuchMethodException {
  // Find Waldo from file:/someLocation/waldo.jar.  Prove that works.
  URL waldosJar = new File("/someLocation/waldo.jar").toURI().toURL();
  assertNotNull(waldosJar);
  assertEquals("file", waldosJar.getProtocol());
  String waldosPath = waldosJar.getPath();
  assertNotNull(waldosPath);
  assertTrue(waldosPath.endsWith("/waldo.jar"));

  ClassLoader cl = new URLClassLoader(new URL[] { waldosJar }, this.getClass().getClassLoader());

  Class<?> waldosClass = cl.loadClass("com.foobar.Waldo");
  assertNotNull(waldosClass);
  assertEquals("com.foobar.Waldo", waldosClass.getName());
  assertSame(cl, waldosClass.getClassLoader());

  Class<?> jimbosClass = cl.loadClass("com.foobar.Jimbo"); // Note: works
  assertNotNull(jimbosClass);

  // Find Waldo from jar:file:/someLocation/waldo.jar!/.  Prove that works.
  // This URL, when passed to a URLClassLoader, should result in the same
  // functionality as the first one.  But it doesn't.
  waldosJar = new URL("jar:" + waldosJar.toExternalForm() + "!/");
  assertEquals("jar", waldosJar.getProtocol());
  assertEquals("file:" + waldosPath + "!/", waldosJar.getPath());

  cl = new URLClassLoader(new URL[] { waldosJar }, this.getClass().getClassLoader());

  waldosClass = cl.loadClass("com.foobar.Waldo");
  assertNotNull(waldosClass);
  assertEquals("com.foobar.Waldo", waldosClass.getName());
  assertSame(cl, waldosClass.getClassLoader());

  jimbosClass = cl.loadClass("com.foobar.Jimbo"); // XXX FAILS
}

UPDATE 0:问题可能与引用jar文件的两个URL之间的假定但非实际等价有关。例如,以下两个URL应该引用同一个文件:

  • file:/myjar.jar
  • jar:file:/myjar.jar!/

当我将从第一种格式构建的URL传递给我的机器时,我认为事情正在发挥作用。当我传递从第二种格式构建的URL时,我得到下面描述的结果。我正在测试更多,以确认这一切毫无疑问。

原始问题

(我知道this question。)

我有一个jar文件waldo.jar,其中包含META-INF/MANIFEST.MF,如下所示:

Manifest-Version: 1.0
Class-Path: jimbo.jar

它还有一个位于其中的位置:

com/foobar/Waldo.class

该类的源代码基本上是:

package com.foobar;

public class Waldo {
  public Jimbo getJimbo() {
    return null;
  }
}

接下来,在同一目录中,我有一个jar文件jimbo.jar,其中包含一个位于其中的以下位置的类:

com/foobar/Jimbo.class

该类的源代码基本上是:

package com.foobar;

public class Jimbo {

}

现在我构建一个URLClassLoader,其网址为waldo.jar。要进行审核:jimbo.jar包含Jimbo并且“在waldo.jar旁边,并列在waldo.jar的{​​{1}}的{​​{1}}标题中适当。 META-INF/MANIFEST-MF包含Class-Path,其中包含对waldo.jar的代码引用。和我一起到目前为止?

我可以加载Waldo就好了。但是,如果我对Jimbo执行涉及com.foobar.Waldo的操作,例如调用Waldo,则会获得com.foobar.Jimbo。这是一个示例堆栈:

Waldo.class.getDeclaredMethod("getJimbo")

这告诉我NoClassDefFoundError在所有情况下都没有正确查询java.lang.NoClassDefFoundError: com/foobar/Jimbo at java.lang.Class.getDeclaredMethods0(Native Method) at java.lang.Class.privateGetDeclaredMethods(Class.java:2701) at java.lang.Class.getDeclaredMethod(Class.java:2128) at com.foobar.TestClassLoadingProblems.wheresWaldo(TestClassLoadingProblems.java:115) Caused by: java.lang.ClassNotFoundException: com.foobar.Jimbo at java.net.URLClassLoader.findClass(URLClassLoader.java:381) at com.foobar.MyClassLoader.findClass(...) // calls super.findClass() at java.lang.ClassLoader.loadClass(ClassLoader.java:424) 标题(我知道how it works in general)。

任何人都可以了解这里发生的事情吗?

1 个答案:

答案 0 :(得分:0)

这是由于bug in the JDK