如何以正确的顺序从jar文件加载类

时间:2014-11-29 11:08:02

标签: java classloader java-bytecode-asm

我编写了一个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类。

1 个答案:

答案 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来加载类。

让我们假设类加载器不知道AB,但它有一个方法来显式加载由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( ... );
  }
}