Nachladen von Klassen zur Laufzeit

Detlef Bosau

Mitglied
Ich stelle die Frage mal hier, ich habe das nie wirklich mal nachgelesen. In C++ kann ich (iirc je nach OS und Laufzeitumgebung) Bibliotheken, also damit auch Klassen, zur Laufzeit eines Programms dynamisch nachladen.

Ist dies in Java auch vorgesehen? Oder müssen alle benötigten Klassen beim Programmstart im Klassenpfad liegen?
 
Das geht mit Java via Reflection. Ich habe es zwar dynamisch zur Laufzeit nicht gemacht, es das sollte ein Anfang sein:


Java:
URLClassLoader cl = URLClassLoader.newInstance(new URL[] {myJarFiles});
Class myClass = cl.loadClass("<Klasse angeben>");
Method method= myClass.getMethod("machMethode", new Class[] {String.class, String.class});
Object myClassObj = myClass.newInstance();
Object response = method.invoke(myClassObj, "String1", "String2");

Du wirst in dem aufrufenden Stück Code ein paar Interfaces definieren müssen, damit du hier mit weniger Reflection auskommst.
 
Zuletzt bearbeitet:
Im Prinzip kannst du damit auch wieder Bibliotheken entladen und auch verschiedene Versionen einer Bibliothek gleichzeitig nutzen.
Wobei das auch mit Hilfe des normalen Classloaders geht. Einfach mehrere Classloader und die Library (ggf. in unterschiedlichen Versionen) laden.

Das "Entladen" läuft genau so, wie bei allen Instanzen. Da darf es dann halt keine Referenzen mehr geben, damit der GC aktiv wird. Das Bedeutet also: Alle Instanzen von Klassen sowie der ClassLoader müssen ohne Referenz sein, damit der GC aktiv wird.

Das nur ganz am Rande - 1999 / 2000 rum habe ich sowas mal gebaut 🙂
 
Also der TE hat das nicht mit gefordert. Beim Vergleich mit C++ ist das doch deutlich: Da wird er ja auch auf Libraries zugreifen und nicht erst irgendwelche Projekte bauen.

Die Antwort #8 ist also mehr spezifisch für Dich, weil ich das als zusätzliche Frage / Interesse von Dir verstanden habe.

Aber das muss man nicht diskutieren - Möglichkeiten sind benannt - unabhängig davon ob der TE das braucht oder nicht.
 
Wenn ich die Anwendungslogik bereits compiliert vorliegen habe, dann macht es keinen Sinn, die Klassen erst im Nachhinein zu laden.
Wieso denn das nicht? Ich kann mir viele Szenarien vorstellen, wo sowas Sinn macht. Man muss sich nur die ganze Thematiken anschauen, wo man mit Plugins arbeitet.

Wenn man sich etwas aus den Fingern saugen muss, dann nimmt man einfach die IntelliJ Plugins: Man kann ein Plugin herunter laden und es ist direkt aktiv. Man muss IntelliJ nicht neu starten. (Und das Laden einer neuen Version erfordert ein Neustart - da ist halt das Thema "Entladen" nicht umgesetzt worden (Das macht halt einiges deutlich komplizierter - das macht aus Meiner Sicht auch wenig Sinn vom Nutzen/Kosten Aspekt her!)
 
Wenn ich die Anwendungslogik bereits compiliert vorliegen habe, dann macht es keinen Sinn, die Klassen erst im Nachhinein zu laden. Insofern habe ich seine Frage so verstanden, dass er beides will...
Ich werde es nicht in näherer Zeit brauchen.

Und jein, die Anwendungslogik muß nicht zwingend ganz fertig sein, im Grunde rede ich vom Anwendungsfall einer "abstract factory". Es ist gewiss 10 Jahre oder länger her, daß ich damit mal gespielt habe, aber da kannst du meinethalben bei laufendem Programm eine Bibliothek von einer webseite laden und ins Programm mit reinschieben. das wurde damals aber nicht von allen Unices unterstützt. BSD, Linux ja, bei system v weiß ich es nicht.
 
Ich bringe mal ein Stück c++ um deutlich zu machen, wo es bei mir im Kopf gerade hängt. (Irgendwie habe ich es überlebt, habe wohl bald 15 Jahre kein c++ ,mehr gemacht.)

C++:
#include <iostream>

struct s {
  int a; int b;
  s(){
    std::cout << "Hello World!" ;
    std::cout << std::endl;
  };
} anon ;
int main() {

  return 0;
}

Die main Methode macht nichts. (Präziser: Sie macht nichts und meldet brav, daß dabei nichts schief gegangen ist.)

Die "Arbeit" macht die Definition anon. Diese Definition führt dazu, daß ein Objekt der Klasse s erzeugt wird. Diese Klasse s könnte jetzt eine concrete factory sein und der Konstruktor würde diese concrete factory etwa in einer Registry einfügen und ein Programm könnte die dort abgreifen und dann mit concrete_factory.getInstance(); die gewünschte Instanz bekommen.

In "Java Denk" wäre dieses "anon'" eine Instanz, die mit der Klasse mitgeliefert wird. Gewissermassen eine "statische Instanz". Und wesentlich ist, daß ich dabei dem Constructor Code unterschiebe.

Wie macht das ein classloader?
 
Also damit etwas passiert muss aber ja auch dort erst eine "Instanz" erzeugt werden. Das ist halt bei Dir nur der Fall, da das dort ja ausgeführt wird.

In Java musst Du also auch erst dafür sorgen, dass eine Klasse geladen wird. Sonst wird da nichts ausgeführt.

Ohne ganz genau zu wissen, was Du willst, wäre meine Vermutung jetzt, dass Du vermutlich etwas willst, was der ServiceLoader macht. (Weshalb @mihe7 den auch noch erwähnt hat). Dann kannst Du Dir einen Service-Namen überlegen. Die jar Datei kann dann dazu eine Klasse beinhalten. Der Service Eintrag besagt dann in erster Linie, welche Klasse den Service bereit stellt.

Die Klasse ServiceLoader enthält auch eine Übersicht, wie das aussehen könnte:
ServiceLoader (Java Platform SE 8 ) (oracle.com)
 
Also damit etwas passiert muss aber ja auch dort erst eine "Instanz" erzeugt werden. Das ist halt bei Dir nur der Fall, da das dort ja ausgeführt wird.

In Java musst Du also auch erst dafür sorgen, dass eine Klasse geladen wird. Sonst wird da nichts ausgeführt.

Ohne ganz genau zu wissen, was Du willst, wäre meine Vermutung jetzt, dass Du vermutlich etwas willst, was der ServiceLoader macht. (Weshalb @mihe7 den auch noch erwähnt hat). Dann kannst Du Dir einen Service-Namen überlegen. Die jar Datei kann dann dazu eine Klasse beinhalten. Der Service Eintrag besagt dann in erster Linie, welche Klasse den Service bereit stellt.

Die Klasse ServiceLoader enthält auch eine Übersicht, wie das aussehen könnte:
ServiceLoader (Java Platform SE 8 ) (oracle.com)
Zumindest hilft das weiter. Laden mußt du die Klassen in C++ natürlich auch, die liegen dann in .so Dateien oder sowas und du mußt die zur Laufzeit dann laden. Das elegante ist, daß sich eine Factory dann selber etwa in einer Registry eintragen kann und damit zur Verfügung steht. Und jetzt möchte ich verstehen, wie ich das in Java nachbilden kann.
 
Das elegante ist, daß sich eine Factory dann selber etwa in einer Registry eintragen kann und damit zur Verfügung steht. Und jetzt möchte ich verstehen, wie ich das in Java nachbilden kann.
In Java läuft sowas über Service Provider Interfaces (SPI) im Zusammenhang mit dem ServiceLoader.

Du definierst ein Interface, das die Methoden deklariert, die das "Plugin" implementieren muss. Das Plugin wiederum implementiert das Interface. "Registriert" wird das über eine simple Textdatei, die sich im Plugin im Verzeichnis META-INF/services befinden und den full qualified name des Interfaces tragen muss. Inhalt ist dann der full qualified name der Implementierung.

Über den ServiceLoader kannst Du nun einfach Instanzen von Implementierungen des Interfaces (sprich: Service-Instanzen) abrufen. Das ist aber alles in der Doku zu ServiceLoader beschrieben.

Verbunden mit dem URLClassLoader (oder Varianten davon) ergibt dann ein Plugin-System, bei dem man zur Laufzeit Plugins hinzufügen kann.
 
In Java läuft sowas über Service Provider Interfaces (SPI) im Zusammenhang mit dem ServiceLoader.

Du definierst ein Interface, das die Methoden deklariert, die das "Plugin" implementieren muss. Das Plugin wiederum implementiert das Interface. "Registriert" wird das über eine simple Textdatei, die sich im Plugin im Verzeichnis META-INF/services befinden und den full qualified name des Interfaces tragen muss. Inhalt ist dann der full qualified name der Implementierung.

Über den ServiceLoader kannst Du nun einfach Instanzen von Implementierungen des Interfaces (sprich: Service-Instanzen) abrufen. Das ist aber alles in der Doku zu ServiceLoader beschrieben.

Verbunden mit dem URLClassLoader (oder Varianten davon) ergibt dann ein Plugin-System, bei dem man zur Laufzeit Plugins hinzufügen kann.
 
Ich habe es noch nicht im einzelnen nachvollzogen, aber der Service Loader kann mir eine Instanz der geforderten Klasse liefern? Das ist natürlich sehr elegant, da muss ich also gar nicht eine geeignete Factory bauen, der Service Loader hat das schon im, Bauch?
 
Ich denke der Service Loader ist nicht unbedingt was du suchst. Du hast nach dynamischen Nachladen außerhalb des Classpath gefragt. Um den Service Loader zu nutzen müssen aber die Klassen bereits dem Classpath/Classloader bekannt sein und geladen werden können. Service Loader initialisieren aber lediglich eine konkrete Instanz hinter einer Schnittstelle. Also eine Art Plugin-System. Mit der Frage aus #1 hat das aber nur bedingt zu tun.
 
Ich habe es noch nicht im einzelnen nachvollzogen, aber der Service Loader kann mir eine Instanz der geforderten Klasse liefern? Das ist natürlich sehr elegant, da muss ich also gar nicht eine geeignete Factory bauen, der Service Loader hat das schon im, Bauch?
Ja, so in der Art.

Hast Du Dir den ServiceLoader mit dem Beispiel in der Dokumentation einmal angesehen? Dort ist ja das Beispiel eines Services com.example.CodecSet gegeben. Das wäre also die Klasse, von der dann die Service Klassen erben sollen.

Die jar Datei enthält dann die Datei META-INF/services/com.example.CodecSet mit dem Namen der Implementation.

Nun kann der ServiceLoader genau so eine Instanz erzeugen. Wichtig ist: Das ist in der Regel erst die Klasse, die dann gewisse andere Implementationen holen kann. So ist das Beispiel ein getEncoder / getDecoder mit einem Parameter.

Also kann man sich alle implementierenden Services holen, und alle nach einem bestimmten Parameter fragen.

Ein Beispiel, wo das z.B. auch angewendet wird, sind jdbc Treiber. Es gibt dann Services, die sozusagen einen Datenbank Treiber laden können. Und dann kann also jeder Treiber gefragt werden: Hast Du einen Treiber für xyz? (Also z.B. für mysql. Der postgres Treiber wird dann sagen: "Hab ich nicht", aber der mysql Treiber wird sagen: "Jo, nimm den hier!" (Das war jetzt extrem bildlich aber ich denke, du hast das Bild damit bekommen.)

Generell sind das aber zwei Paar Schuhe:
a) Die Klassen erst einmal selbst laden können.
b) dann den Service verfügbar machen.

Der erste Punkt a ist also die Erzeugung eines ClassLoaders, der dann die zusätzlichen Klassen lädt.
Der zweite Punkt nutzt die von @mihe7 in #10 verlinkte Methode load, die auch den ClassLoader als Parameter übergeben bekommt.


An der Stelle auch noch der Hinweis: in #3 gab es auch schon den Hinweis von @Oneixee5 zu OSGi.

Die Frage ist halt, was genau Du brauchst.
Man kann sich hier das eine oder andere selbst bauen. Bei einfachen Ansprüchen geht es ganz schnell und es ist nicht besonders komplex. Sobald aber die Ansprüche steigen, dann kann man sich überlegen, ob ein OSGi Container (Eclipse Equinox, Apache Felix oder Apache Karaf) und die Erzeugung von OSGi Bundles (das kann schon ein einfaches maven-bundle-plugin mit minimaler Config sein) nicht ein besserer Ansatz sein könnte. Aber es kommen halt doch deutliche Abhängigkeiten und auch etwas mehr Komplexität hinzu. (Aber es ist eine bekannte Komplexität. Du hast da nicht etwas selbstgebautes und da muss man erst rein kommen, sondern man hat etwas vor sich, das ggf. bekannt ist und wenn nicht, dann kann man sich da gezielt schnell einarbeiten.)
 
Derzeit brauchen tue ich es gar nicht, es geht mir gerade um eine gewisse Vergangenheitsbewältigung, das würde hier zu weit führen. Ich habe das SPI gestern nur quergelesen, und ja, das leistet wohl das gewünschte.

Stand das in Java von Anfang an zur Verfügung oder wurde es erst später eingeführt, wenn ja: wann?
 
Man hat einfach die Klassen / Ressourcen, die man braucht, geladen.

Bei alten JDBC Dokumentationen findet man das z.B. noch. Da gab es dann ein Class.forName("a.b.c.WhateverDriver");
 

Neue Themen


Zurück
Oben