Kurze Einführung in ClassLoader – Teil 3

Ein eigener ClassLoader muss von java.lang.ClassLoader ableiten und sollte die findClass-Methode überschreiben. Diese Methode benötigt den vollständigen Klassennamen und gibt die geladene Klasse in Form eines Class Objektes zurück.

Der nachfolgende ClassLoader MyClassLoader lädt die Klassen aus einem übergebenen Dateipfad. Wie in Java üblich, wird die Packagestruktur auf das Filesystem übertragen (d.h. die Klasse ch.javablog.reloadable.MyTest liegt unter ../ch/javablog/reloadable).

Im Konstruktor von MyClassLoader wird der “parent class loader” übergeben, damit der Delegationsmechanismus funktionieren kann. Erst wenn der “parent class loader” die zu ladende Klasse nicht finden konnte, wird unsere findClass-Methode aufgerufen. Dort lesen wir das “.class”-File ein und erzeugen ein byte-Feld (Zeile 23-29).

Dieses byte-Feld wird für die Methode defineClass der Oberklasse benötigt, denn sie macht die eigentliche Arbeit und generiert aus dem byte-Feld eine Java Klasse :)

ch.javablog.MyClassLoader:

  1. public class MyClassLoader extends ClassLoader {
  2.     private final String startPath;
  3.     private final String fileSeparator = System.getProperty("file.separator");
  4.     public MyClassLoader(ClassLoader parent, String startPath) {
  5.         super(parent);
  6.         this.startPath = startPath;
  7.     }
  8.  
  9.     protected Class findClass(String name) throws ClassNotFoundException {
  10.         // Absoluten Pfad zur Klasse finden
  11.         StringBuffer path       = new StringBuffer(this.startPath);
  12.         String[] splittedName   = name.split("\\\\.");
  13.         List splittedNameList   = Arrays.asList(splittedName);
  14.  
  15.         Iterator it = splittedNameList.iterator();
  16.         while (it.hasNext()) {
  17.             String pathFragment = (String) it.next();
  18.             path.append(fileSeparator + pathFragment);
  19.         }
  20.         path.append(".class");
  21.         System.out.println("MyClassLoader - loading: " + path.toString());
  22.         // Klasse aus Datei laden
  23.         File classFile = new File(path.toString());
  24.         int fileLength = (int) classFile.length();
  25.         byte[] fileContent = new byte[fileLength];
  26.         FileInputStream fis = null;
  27.         try {
  28.             fis = new FileInputStream(classFile);
  29.             fis.read(fileContent);
  30.  
  31.             // Erzeugt aus dem byte Feld ein Class Object.
  32.             return super.defineClass(name, fileContent, 0, fileLength);
  33.         } catch (Exception e) {
  34.             throw new ClassNotFoundException(e.toString());
  35.         } finally {
  36.             if (fis != null) {
  37.                 try {
  38.                     fis.close();
  39.                 } catch (Exception ignored) {
  40.  
  41.                 }
  42.             }
  43.         }
  44.     }
  45. }

In einer kleinen Main-Methode probieren wir MyClassLoader aus. Wichtig: Die Klasse ch.javablog.reloadable.MyTest befindet sich nicht im CLASSPATH, sondern in dem Verzeichnis “myclasses” (parallel zum “classes” Verzeichnis).

classes und myclasses Verzeichnis liegen auf der selben Ebene

ch.javablog.Main :

  1. public static void main(String[] args) throws Exception{
  2.         ClassLoader clCurrent = Thread.currentThread().getContextClassLoader();
  3.         ClassLoader clNew = new MyClassLoader(clCurrent, "." + fileSeparator + "myclasses");
  4.         Class myTest = clNew.loadClass("ch.javablog.reloadable.MyTest");
  5.         System.out.println(myTest + " classloader: " + myTest.getClassLoader());
  6.     }

Ausgabe:
MyClassLoader - loading: .\myclasses\ch\javablog\reloadable\MyTest.class
class ch.javablog.reloadable.MyTest classloader: ch.javablog.MyClassLoader@923e30

Interpretation:
Die MyTest wurde erfolgreich von MyClassLoader geladen. Wenn MyTest weitere Klassen importiert, werden sie ebenfalls von MyClassLoader geladen. Die untere Abbildung zeigt die Position unseres ClassLoaders in der ClassLoader-Hierarchie.

ClassLoader Struktur Teil 3

Im nächsten Teil geht es um die ClassLoader im Apache Tomcat, und warum er einen anderen Delegationsmechanismus einsetzt.

Bookmark and Share

2 Responses to “Kurze Einführung in ClassLoader – Teil 3”

  1. Toni Menzel Says:

    Hi!
    Sehr schönes Einstiegs-Tutorial in Java’s ClassLoader Prinzip,
    Eine kleine Sache:
    Der Parameter für String.split(..) im MyClassLoader Zeile 12 muss “\\.” sein.
    Der Backslash escaped den zweiten Backslash, sodass der Regex “\.” bekommt und nicht nur “.”
    btw. “\.” geht auch net..

    Schöne Grüße aus Hannover (de),
    Toni

  2. HeikoMaass Says:

    Hallo Toni
    Hier hat das Wordpress-Plugin den zweiten Backslash fälschlicherweise nicht escaped. Ist nun korrigiert :)
    Vielen Dank für den Hinweis.

    Beste Grüsse aus St. Gallen
    Heiko

Leave a Reply