2016-06-24 26 views
0

Ich habe eine ziemlich komplexe Java-Software, die dynamisch eine Klasse laden, ein Objekt erstellen und eine seiner Methoden aufrufen muss. Es stellt sich heraus, dass dies in einigen Situationen eine NoClassDefFoundError (verursacht durch eine ClassNotFoundException) verursacht, wenn die geladene Klasse eine andere verweist.Warum funktioniert dieser URLClassLoader manchmal und manchmal nicht?

Sagen wir:

public interface Go { 
    void go(); 
} 

Sagen Sie, dass die Klasse geladen werden:

package foo; 
import static baz.Baz.BAZ; 
public class Foo implements Go { 
    @Override 
    void go() { 
     ... 
     something = BAZ; 
     ... 
    } 
} 

wo diese Art und Weise wird

package baz; 
public class Baz { 
    public static final int BAZ = 111; 
    ... 
} 

Sagen Sie, dass die Klasse geladen:

try (final URLClassLoader loader = new URLClassLoader(new URL[] { url })) { 
    final Class<? extends Foo> clazz = 
     loader.loadClass("foo.Foo").asSubclass(Go.class); 
    this.obj = clazz.newInstance(); 
    this.obj.go(); 
} catch ... 

Dann funktioniert alles. Sagen Sie, dass Sie stattdessen Dinge diese andere Art und Weise zu tun:

try (final URLClassLoader loader = new URLClassLoader(new URL[] { url })) { 
    final Class<? extends Foo> clazz = 
     loader.loadClass("foo.Foo").asSubclass(Go.class); 
    this.obj = clazz.newInstance(); 
    //we do not invoke go() now... 
} catch ... 

//later, in another method... 
this.obj.go(); 

nun der Aufruf von go() wirft ein NoClassDefFoundError, beschwert es nicht baz.Baz laden kann. Natürlich ist die URL die gleiche. Natürlich befinden sich sowohl foo.Foo als auch baz.Baz in dem Pfad, der durch url angegeben ist. Der einzige Unterschied, den ich sehen kann, ist der Moment, wenn die Methode go() aufgerufen wird.

Was ist hier falsch?

+0

Es fehlen Informationen: Welches Paket ist Go in? Wie wird die URL an den URLClassloader übergeben? Und akzeptiert der Compiler das Laden von Foo als Unterklasse Go und dann Zuweisen von ''? erstreckt sich Foo'''? –

Antwort

2

Was ist hier falsch?

Ich denke, dass das Problem ist folgendes:

try (final URLClassLoader loader = new URLClassLoader(new URL[] { url })) { 

Das ist der Klassenlader geschlossen werden verursacht, wenn Sie den Try-Block verlassen. Die close() Methode der javadoc sagt:

diesen URLClassLoader schließt, so dass es nicht mehr verwendet werden kann, neue Klassen oder Ressourcen zu laden, die von diesem Lader definiert sind.

Aufruf der go() Methode Klasse Initialisierung für die Go Klasse auslöst, und das ist, was die Ursache der Baz Klasse geladen und initialisiert werden. Wenn das Laden der letzteren Klasse auftritt, nachdem der Klassenlader geschlossen wurde, wird es fehlschlagen.

+0

Whoops. Du hast recht. –

1

Wenn eine Klasse von anderen Klassen abhängt, sind diese faul geladen.

Das bedeutet, Laden foo.FOO lädt baz.BAZ nicht gleichzeitig. Letzterer wird beim ersten Zugriff geladen. In Ihrem Fall lädt auch der Methodenaufruf go() es.

In Ihrem zweiten Beispiel zu dem Zeitpunkt, wenn die Methode go() genannt wird, ist die URLClassLoader bereits geschlossen - durch die Try-with-Ressourcen-Anweisung. Daher kann es keine Klassen mehr laden.