问题是,出于某种奇怪的原因,我必须用子 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 。