2014-02-23 16 views
8

, der Klassenlader, der das Laden einer Klasse initiiert, wird von der JVM als initiierender Klassenlader aufgezeichnet. Weiterhin wird gemäß der JavaDoc von ClassLoader#findLoadedClass() die MethodefindLoadedClass() gibt null zurück

Gibt die Klasse mit dem angegebenen Namen der Binärdatei wenn diese Lader durch die Java Virtual Machine als initiierende loader einer Klasse mit dem Namen der Binärdatei aufgezeichnet wurde.

(Hervorhebung von mir)

Betrachten wir ein einfaches Klassenlader

class SimpleClassLoader extends ClassLoader { 
    void foo() { 
     System.err.println(loadClass("foo.Bar")); 
     System.err.println(findLoadedClass("foo.Bar")); 
    } 
} 

Da foo.Bar existiert tatsächlich im Klassenpfad, new SimpleClassLoader().foo() druckt

class foo.Bar 
null 

die Gründe Nach gegeben oben, sollte SimpleClassLoader der initiierende Klassenlader und 012.350.sollte nur die erfolgreich geladene Klasse zurückgeben.

nun diese zweite Version betrachten:

class SimpleClassLoader2 extends ClassLoader { 
    SimpleClassLoader2() { 
     super(null); // disables delegation 
    } 
    protected Class<?> findClass(String name) { 
     try { 
      byte[] b = IOUtils.toByteArray(new FileInputStream("path/to/foo/Bar.class")); 
      return defineClass("foo.Bar", b, 0, b.length); 
     } catch (Exception e) { 
      e.printStackTrace(); 
      return null; 
     } 
    } 
    void foo() { 
     System.err.println(loadClass("foo.Bar")); 
     System.err.println(findLoadedClass("foo.Bar")); 
    } 
} 

Diese SimpleClassLoader2 sowohl die Initiierung sowie die Definition von Klassenlader von foo.Bar macht. Tatsächlich jetzt new SimpleClassLoader2().foo() die

class foo.Bar 
class foo.Bar 

So gewünschten druckt entweder die Dokumentation ist falsch oder ich verstehe nicht, warum SimpleClassLoader nicht als initiierende Klassenlader von foo.Bar angesehen wird. Kann jemand bitte etwas Licht in das werfen?

+0

Welchen Klassenlader erben Sie? Ich frage, weil im ersten Fall von SimpleClassLoader würde ich eine ClassNotFoundException erwarten (gegeben findLoadedClass ruft findClass, aber ich bin mir nicht sicher). –

+0

Dies ist nicht der Fall, weil das Delegieren zuerst an das übergeordnete Element erfolgt. Wenn ich in der ersten Version den Elternwert auf null setzen würde, wäre das korrekt. – musiKk

+0

Die Standardimplementierung von findClass wirft die Ausnahme auf: http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/lang/ClassLoader.java#ClassLoader. findClass% 28java.lang.String% 29 –

Antwort

3

Ich habe einige weitere Tests und ich bin ziemlich sicher, dass die Spezifikation korrekt implementiert. Mein Fehler war, dass das reflektive Laden einer Klasse dasselbe ist wie das Laden als Teil des Auflösungsschrittes. Es ist sinnvoll: Sowohl die Spezifikation und die JavaDoc Erwähnung „Aufnahme“ einen Klassenlader als initiierende Klassenlader. Wenn ich selbst loadClass() aufrufen, kann die VM nicht wissen, welcher Klassenlader der initiierende Klassenlader sein soll, daher wird der definierende Klassenlader trivialerweise auch der initiierende Klassenlader.

Dies kann, indem die geladenen Klasse Trigger Laden einer anderen Klasse (foo.Baz) als Teil der Auflösung von Abhängigkeiten gezeigt werden, sondern eine andere Klassenlader die tatsächliche Belastung zu tun haben. *

* Ich bin ziemlich sicher, das ist nicht korrektes Verhalten eines gültigen Klassenlader. Ich mache es nur einen Punkt zu illustrieren.

Betrachten Sie die folgenden Klassen (sie sind alle in Paket foo):

public class Bar { 
    public Bar() { 
     new Baz(); 
    } 
} 

und

public class Baz { 
} 

Meine benutzerdefinierte Class Loader jetzt leicht modifiziert:

public class SimpleClassLoader extends ClassLoader { 

    static final String PATH = "/path/to/classes"; 

    public SimpleClassLoader() { 
     // disable parent delegation 
     super(null); 
    } 

    public void printLoadedClass(String name) throws Exception { 
     Class<?> cls = findLoadedClass(name); 
     System.err.println("findLoadedClass(" + name + ") = " + cls 
       + ", has class loader " + cls.getClassLoader()); 
    } 

    @Override 
    protected Class<?> findClass(String name) throws ClassNotFoundException { 
     if (name.equals("foo.Baz")) { 
      // don't want to be defining class loader of foo.Baz 
      return getSystemClassLoader().loadClass(name); 
     } 
     // now we're loading foo.Bar 
     try { 
      byte[] b = IOUtils.toByteArray(new FileInputStream(PATH + "/foo/Bar.class")); 
      return defineClass(name, b, 0, b.length); 
     } catch (ClassFormatError | IOException e) { 
      e.printStackTrace(); 
      throw new ClassNotFoundException(); 
     } 
    } 
} 

Die Test ist einfach:

public static void main(String[] args) throws Exception { 
    SimpleClassLoader cl = new SimpleClassLoader(); 
    Class<?> cls = cl.loadClass("foo.Bar"); 
    cls.newInstance(); // this triggers resolution 

    cl.printLoadedClass("foo.Bar"); 
    cl.printLoadedClass("foo.Baz"); 
} 

Ausgang ist

findLoadedClass(foo.Bar) = class foo.Bar, has class loader [email protected] 
findLoadedClass(foo.Baz) = class foo.Baz, has class loader [email protected] 

Wie man sehen kann: SimpleClassLoader initiiert das Laden von und definiert auch foo.Bar. Beim Erstellen der Instanz wird die Auflösung foo.Baz ausgelöst. Dieses Mal wird die Definition der Klasse an den Systemklassenlader delegiert, sodass sie zum definierenden Klassenlader wird. Die Ausgabe zeigt, dass SimpleClassLoader Klassenlader für beide Klassen initiiert, aber nur die erste Klasse definiert.