1

课程:

public interface Inter {
  ...some methods...
}

public class Impl implements Inter {
  ...some implementations...
}

问题是由于某种奇怪的原因,我必须用子 ClassLoader 加载接口InterImplClassLoader加载实现类。

在这种情况下,我会得到NoClassDefError,因为尝试加载实现的父 ClassLoaderImpl不知道Inter在子 ClassLoader 中加载的接口。

有没有办法用子 ClassLoader (context ClassLoader) 加载实现?或者我可能需要编写一些自定义 ClassLoader 来加载它们(通过打破委托规则)?

4

2 回答 2

3

问题是,出于某种奇怪的原因,我必须用子 ClassLoader 加载接口 Inter,用父 ClassLoader 加载实现类 Impl。

我无法理解为什么子类加载器必须加载接口,而让父类加载器加载实现。这势必会造成麻烦,因为JVM 使用的类加载机制中没有将类的加载推迟到子类加载器的机制。在 JVM 中实现类加载行为的常用机制在 ClassLoader 类的 API 文档中定义:

ClassLoader 类使用委托模型来搜索类和资源。ClassLoader 的每个实例都有一个关联的父类加载器。当请求查找类或资源时,ClassLoader 实例将在尝试查找类或资源本身之前将对该类或资源的搜索委托给其父类加载器。虚拟机的内置类加载器称为“引导类加载器”,它本身没有父级,但可以作为 ClassLoader 实例的父级。

可以通过扩展ClassLoader 类并覆盖loadClass() 方法来编写自定义类加载器。扩展此方法允许您以两种方式之一更改类加载委托:

  • Parent-first : 让父类加载器先加载类。这通常是一种传递行为——大多数父类加载器会将加载推迟到它们的父类,依此类推,直到在类加载器层次结构中到达引导类加载器(根)。如果父类加载器无法加载类,子类会尝试加载它。加载类的最终失败应导致抛出 ClassNotFoundException。
  • Parent-last:自定义类加载器尝试先加载类,然后再委托给父类。仅当子加载类失败时才使用父类加载器。

大多数类加载器都是作为父类加载器实现的。这是因为委托机制能够向上遍历树,但不能向下遍历树。

如果您真的希望将类的加载和查找委托给层次结构中的子类加载器,则必须在父类的自定义类加载器中管理对它们的引用。这并不容易,除了非常特殊的情况外,通常根本不会这样做,因为很容易以可怕的 ClassNotFoundException 和 NoClassDefFoundError 告终,因为必须小心仅从子类加载器加载所需的类,而其余的必须总是被推迟到父级(除非我弄错了,某些 Java EE 容器中共享库的特性是这样实现的)。

话虽如此,理想的解决方案是尝试在父类加载器中同时加载接口和实现类,并依靠委托机制确保类对两个类加载器可见;父级可以“看到”自己加载的类,子级可以“看到”父级的类。

PS:在加载和定义类时不要忘记使用AccessController.doPrivileged 。

于 2010-08-18T09:05:44.277 回答
0

您是否覆盖 java.lang.ClassLoader 的 loadClass(String name,boolean resolve)?

我请你做了。

加载的默认行为首先加载父类文件,并将其标记为已加载,如果找到 loadClass 将首先加载已加载的类,这就是我客人覆盖的方式,并且没有在自定义类加载器中调用 super.loadClass 方法。

于 2010-08-18T08:21:28.860 回答