我编写了一个Java类加载器来从jar文件中加载类。
Enumeration<JarEntry> entries = jarFile.entries();
while (entries.hasMoreElements()) {
JarEntry element = entries.nextElement();
if (element.getName().endsWith(".class")) {
//Class Manipulation via ASM
//Loading class with MyClassloader
}
}
问题是: 当我加载一个类,从同一个Jar中的一个类中的Sublcass并且尚未加载子类时,我得到一个ClassNotFoundException。
示例:
class A extends B{}
Class B{}
由于按字母顺序排列,首先加载A类。我为类B得到了ClassNotFoundException。此时,没有加载B类。
答案 0 :(得分:3)
我假设您的类加载是作为某种推送类文件执行的。但是你应该拉它们。为了解释我的意思,让我们看一下普通Java类加载的简短例子:
class Main {
public static void main(String[] args) {
new B();
}
}
class B extends A { }
class A { }
创建new B()
时,Main
的类加载器基本上执行classLoader.loadClass("B")
。此时,B
的超类A
尚未加载。同时,类加载器不能知道B
具有A
作为其超类。因此,类加载器负责在classLoader.loadClass("A")
的类加载完成之前通过询问B
来加载类。
让我们假设类加载器不知道A
或B
,但它有一个方法来显式加载由classLoader.inject(String, byte[])
的外部实体接收的类。然后这个调用序列不会计算:
classLoader.inject("B", bBytes);
classLoader.inject("A", aBytes);
因为在加载B
时,类加载器还不知道A
。
实现自己的类加载器时需要做的是将类存储在某种映射中,并实现类加载器的类加载方法,如:
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] bytes = map.get(name);
if (bytes != null) {
return defineClass(name, bytes, 0, bytes.length);
} else {
throw new ClassNotFoundException(name);
}
}
通过允许类加载器确定加载顺序,您可以完全避免这个问题。
更准确地说,你需要在两个步骤中进行操作和加载,其中伪算法看起来像这样:
Enumeration<JarEntry> entries = jarFile.entries();
MyClassLoader classLoader = new MyClassLoader();
// First we generate ALL classes that the class loader is supposed to load.
// We then make these classes accessible to the class loader.
while (entries.hasMoreElements()) {
JarEntry element = entries.nextElement();
if (element.getName().endsWith(".class")) {
// Class Manipulation via ASM
classLoader.addClass( ... );
}
}
// Now that the class loader knows about all classes that are to be loaded
// we trigger the loading process. That way, the class loader can query
// itself about ANY class that it should know.
while (entries.hasMoreElements()) {
JarEntry element = entries.nextElement();
if (element.getName().endsWith(".class")) {
classLoader.loadClass( ... );
}
}