Interfaces über Reflection laden



  • Hallo,

    ist es möglich alle Interfaces die z.B. "Messages" heißen und in verschiedenen packages liegen, dynamisch zur Laifzeit zu ermitteln.

    Was bietet mir die Reflection-API für Möglichkeiten ?

    Die Intention dahinter ist, das ich an alle Konstanten in diesen
    Interfaces rankommen möchte.

    Es gibt also z.B. folgende Interfaces die ich laden möchte:

    com.test.Messages;
    com.test.test2.Messages;
    com.test.test2.test3.Messages;

    ohne konkrete Implementierungen, da sie nur zur Auslagerung von
    Konstanten dienen.

    Danke für die Hilfe
    Gamba


  • Mod

    Ja, das dürfte möglich sein. Ich mache in meinem Projekt sowas ähnliches. Folgende Klasse dient dazu, alle Klassen zu laden, die von bestimmten Klassen abgeleitet sind. Vielleicht kannst du dir das umschreiben, damit die Klasse für deine Zwecke geeignet ist (Wahrscheinlich mußt größtenteils du nur die Methode "loadClasses (LinkedList classFileList)" etwas verändern.).

    ...die Klasse ist momentan noch nicht in einem Jar-File getestet worden. Es kann also sein, dass es da noch ein paar Fehler gibt.
    [java]
    package controller;

    import java.io.File;
    import java.io.IOException;
    import java.net.URI;
    import java.net.URISyntaxException;
    import java.net.URL;
    import java.util.Enumeration;
    import java.util.Hashtable;
    import java.util.Iterator;
    import java.util.LinkedList;
    import java.util.jar.JarEntry;
    import java.util.jar.JarFile;

    public class DynamicClassLoader
    {
    private LinkedList classList;
    private Hashtable classTable;

    public DynamicClassLoader ()
    {
    classList = new LinkedList();
    classTable = new Hashtable ();
    }

    public void addSuperclass (Class superclass)
    {
    if (classList.contains(superclass)) return;
    classList.add(superclass);
    classTable.put(superclass,new LinkedList());
    }

    public LinkedList getLoadedClasses (Class superclass)
    {
    Object linkedList = classTable.get(superclass);
    if (linkedList != null) return (LinkedList)linkedList;
    return null;
    }

    public void loadAllClasses ()
    {
    URL controllerURL = ClassLoader.getSystemResource("controller");
    String controllerURLString = controllerURL.toString();
    try
    {
    if (controllerURLString.startsWith("jar:"))
    {
    int jarEnd = controllerURLString.indexOf("!");
    String jarString = controllerURLString.substring(4,jarEnd);
    JarFile jarFile;
    jarFile = new JarFile(new File(new URI(jarString)));
    loadClasses (jarFile);
    }
    else
    {
    File controllerDirectory = new File (new URI(controllerURLString));
    File baseDirectory = controllerDirectory.getParentFile();
    loadClasses (baseDirectory);
    }
    }
    catch (IOException e)
    {
    }
    catch (URISyntaxException e)
    {
    }
    }

    public void loadClasses (File baseDirectory)
    {
    LinkedList fileList = new LinkedList();
    getClassFiles (fileList, baseDirectory);
    LinkedList classFileList = new LinkedList();
    final int start = baseDirectory.getAbsolutePath().length() + 1;
    Iterator iterator = fileList.iterator();
    while (iterator.hasNext())
    {
    classFileList.add(((String)iterator.next()).substring(start));
    }
    loadClasses(classFileList);
    }

    private void getClassFiles (LinkedList classFileList, File directory)
    {
    File [] files = directory.listFiles();
    for (int i = 0 ; i < files.length ; ++i)
    {
    String name = files[i].getAbsolutePath ();
    if (name.endsWith(".class"))
    {
    classFileList.add(name);
    }
    else if (files[i].isDirectory())
    {
    getClassFiles (classFileList,files[i]);
    }
    }
    }

    public void loadClasses (JarFile jarFile)
    {
    Enumeration jarEntries = jarFile.entries ();
    LinkedList fileList = new LinkedList();
    while (jarEntries.hasMoreElements())
    {
    JarEntry entry = (JarEntry)jarEntries.nextElement();
    String name = entry.getName();
    if (name.endsWith(".class"))
    {
    fileList.add(name);
    }
    }
    loadClasses(fileList);
    }

    private void loadClasses (LinkedList classFileList)
    {
    final Iterator iterator = classFileList.iterator();
    final char separator = File.separatorChar;
    while (iterator.hasNext())
    {
    String name = (String)iterator.next();
    name = name.substring (0,name.length() - 6);
    name = name.replace('/','.');
    name = name.replace(separator,'.');
    try
    {
    final Class currentClass = Class.forName(name);
    if (currentClass.isInterface()) continue;
    if (currentClass.getConstructors().length == 0) continue;
    final Iterator superclassIterator = classList.iterator();
    while (superclassIterator.hasNext())
    {
    Class superclass = (Class)superclassIterator.next();
    if (superclass.isAssignableFrom(currentClass))
    {
    final LinkedList list = (LinkedList)classTable.get(superclass);
    if (!list.contains(currentClass)) list.add(currentClass);
    }
    }
    }
    catch (ClassNotFoundException e)
    {
    }
    catch (SecurityException e)
    {
    }
    }
    }
    }[/code]



  • Hi Gregor,

    vielen Dank für den Code, aber ich hab noch ein paar Fragen.
    Ich weiß noch nicht genau wie ich alle packages durchsuchen kann,
    ohne ihren Namen zu wissen ? Du machst das, glaube ich, in folgender
    Methode:

    public void loadAllClasses ()
       {
          URL controllerURL = ClassLoader.getSystemResource("controller");
          String controllerURLString = controllerURL.toString();
          try
          {
             if (controllerURLString.startsWith("jar:"))
             {
                int jarEnd = controllerURLString.indexOf("!");
                String jarString = controllerURLString.substring(4,jarEnd);
                JarFile jarFile;
                jarFile = new JarFile(new File(new URI(jarString)));
                loadClasses (jarFile);
             }
             else
             {
                File controllerDirectory = new File (new URI(controllerURLString));
                File baseDirectory = controllerDirectory.getParentFile();
                loadClasses (baseDirectory);
             }
          }
          catch (IOException e)
          {
          }
          catch (URISyntaxException e)
          {
          }
       }
    

    Ich verstehe folgendes Statement noch nicht so ganz und was es genau macht:

    ClassLoader.getSystemResource("controller");
    

    Auch die Java-API hat mir nicht wirklich viel gebracht.

    Ich glaube du versuchst hier irgendwie an das Oberverzeichnis aller
    Klassen dranzukommen, und diese dann entweder in ein Objekt vom Typ
    JarFile oder ein File Objekt zu packen, mit dem du die einzelnen
    Klassen dann lädst. Aber was "controller" oder "jar:" bzw. "!"
    zu sagen hat, verstehen ich noch nicht so ganz.

    Eine kleine Erklärung wäre super,

    Danke
    Gamba

    [ Dieser Beitrag wurde am 03.04.2003 um 10:34 Uhr von Gamba editiert. ]


  • Mod

    Das "controller" ist ein kleiner Trick:

    Guck mal in die allererste Zeile. Da steht:

    "package controller;"

    Das heißt letztendlich, dass sich die Klasse in einem Verzeichnis mit dem Namen "controller" befindet. Dieses Verzeichnis lasse ich mir dann mit getSystemResource geben.

    Die Idee ist letztendlich, dass das Verzeichnis, in dem sich das Verzeichnis "controller" befindet, das "Projektverzeichnis" ist.

    Wenn die URL, die auf das Verzeichnis "controller" verweist, aber mit "jar:" anfängt, dann befindet sich das Projekt in einer Jar-Datei. Entsprechend wird dann die Jar-Datei aus der URL rekonstruiert.

    ...ein bischen tricky! 🙂

    EDIT:

    Vielleicht kann man das auch einfacher hinkriegen, ich weiß nicht, ob es irgendwo etwas in der Standardbibliothek gibt, mit dem man schnell rausfinden kann, ob sich das Projekt in ner Jar-Datei befindet und wo es sich befindet.

    [ Dieser Beitrag wurde am 03.04.2003 um 12:08 Uhr von Gregor editiert. ]



  • Dank Gregor,

    hat alles wunderbar geklappt.

    Das Einzige was mich noch stört, ist, das ich immer
    den ersten package Namen kennen muss, um an die Klassen
    bzw. Interfaces zu kommen.
    Also wenn ich

    ClassLoader.getSystemResource("controller");
    

    aufrufe, oder was ähnliches, benötige ich
    ja immer den ersten Teil des packages (hier:"controller"),
    kann man das nicht irgendwie eleganter lösen ?

    Danke nochmal
    Gamba

    [ Dieser Beitrag wurde am 04.04.2003 um 19:46 Uhr von Gamba editiert. ]


  • Mod

    Original erstellt von Gamba:
    **
    kann man das nicht irgendwie eleganter lösen ?
    **

    Wie schon gesagt: Mir ist nichts eleganteres eingefallen. Es kann aber gut sein, dass das eleganter geht. Vielleicht fällt dazu ja noch jemandem etwas ein und postet es hier. Ich wäre daran auch sehr interessiert.


Anmelden zum Antworten