How to load classes from jar file in correct order

I wrote Java Classloader to load classes from jar file.

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
        }
    }

      

The problem is this: When I load a class that is Sublcass from a class in the same Jar and the subclass has not been loaded yet, I get a ClassNotFoundException.

Example:

class A extends B{}
Class B{}

      

Because of the alphabetical order, class A is loaded first. I get a ClassNotFoundException for class B. At this time, class B is not loaded.

+3


source to share


1 answer


I am assuming that your class loading is done as some type of file pushing. You should get them out quickly. To explain what I mean by this, let's take a look at a short example of normal Java class loading:

class Main {
  public static void main(String[] args) {
    new B();
  }
}
class B extends A { }
class A { }

      

When created, new B()

the classloader Main

basically executes classLoader.loadClass("B")

. At this point, the B

superclass A

has not been loaded yet. At the same time, the classloader cannot know what it B

has A

as its superclass. This way the classloader takes responsibility for loading the class by asking itself classLoader.loadClass("A")

before the class has finished loading B

.

Suppose the classloader did not know about A

either B

, but had a way to explicitly load the classes that it gets from an external object using classLoader.inject(String, byte[])

. This calling sequence will then not be evaluated:

classLoader.inject("B", bBytes);
classLoader.inject("A", aBytes);

      

because when loading B

the classloader doesn't know about A

.



What you need to do when implementing your own classloader is to store the classes in some kind of map and implement the class of the class of the class of the class:

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);
  }
}

      

By letting the class loader determine the loading order, you avoid this problem altogether.

To be more precise, you need to do the manipulation and loading in two steps, where the pseudo-algorithm will look something like this:

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( ... );
  }
}

      

+3


source







All Articles