387 Stimmen

Wie kann man JAR-Dateien dynamisch zur Laufzeit laden?

Warum ist es so schwer, dies in Java zu tun? Wenn Sie irgendeine Art von Modulsystem haben wollen, müssen Sie in der Lage sein, JAR-Dateien dynamisch zu laden. Mir wurde gesagt, dass es einen Weg gibt, dies zu tun, indem man seine eigenen ClassLoader aber das ist eine Menge Arbeit für etwas, das (zumindest meiner Meinung nach) so einfach sein sollte wie der Aufruf einer Methode mit einer JAR-Datei als Argument.

Gibt es Vorschläge für einen einfachen Code, der dies ermöglicht?

314voto

jodonnell Punkte 48623

Der Grund für die Schwierigkeiten ist die Sicherheit. Classloader sollen unveränderlich sein; man sollte nicht in der Lage sein, ihnen zur Laufzeit wahllos Klassen hinzuzufügen. Ich bin eigentlich sehr überrascht, dass das mit dem System-Classloader funktioniert. Hier sehen Sie, wie Sie Ihren eigenen Child-Classloader erstellen:

URLClassLoader child = new URLClassLoader(
        new URL[] {myJar.toURI().toURL()},
        this.getClass().getClassLoader()
);
Class classToLoad = Class.forName("com.MyClass", true, child);
Method method = classToLoad.getDeclaredMethod("myMethod");
Object instance = classToLoad.newInstance();
Object result = method.invoke(instance);

Schmerzhaft, aber so ist es.

151voto

Allain Lalonde Punkte 88365

Die folgende Lösung ist hackish, da sie Reflection verwendet, um die Kapselung zu umgehen, aber sie funktioniert einwandfrei:

File file = ...
URL url = file.toURI().toURL();

URLClassLoader classLoader = (URLClassLoader)ClassLoader.getSystemClassLoader();
Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
method.setAccessible(true);
method.invoke(classLoader, url);

56voto

Mordechai Punkte 14325

Während die meisten hier aufgeführten Lösungen entweder Hacks sind (vor JDK 9), schwer zu konfigurieren (Agenten) oder einfach nicht mehr funktionieren (nach JDK 9), finde ich es wirklich überraschend, dass niemand eine [klar dokumentierte Methode](https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/lang/ClassLoader.html#getSystemClassLoader()) .

Sie können einen benutzerdefinierten System Class Loader erstellen und dann tun, was Sie wollen. Keine Reflexion erforderlich und alle Klassen teilen sich denselben Classloader.

Fügen Sie beim Starten der JVM dieses Flag hinzu:

java -Djava.system.class.loader=com.example.MyCustomClassLoader

Der Classloader muss einen Konstruktor haben, der einen Classloader akzeptiert, der als sein Elternteil festgelegt werden muss. Der Konstruktor wird beim Start der JVM aufgerufen und der echte System-Classloader wird übergeben, die Hauptklasse wird vom benutzerdefinierten Loader geladen.

Um Gläser hinzuzufügen, rufen Sie einfach ClassLoader.getSystemClassLoader() und übertragen Sie es auf Ihre Klasse.

Überprüfen Sie diese Implementierung für einen sorgfältig gestalteten Classloader. Bitte beachten Sie, dass Sie die add() Methode auf öffentlich.

51voto

Martin Klinke Punkte 7148

Werfen Sie einen Blick auf OSGi z.B. implementiert in der Eclipse-Plattform . Sie tut genau das. Sie können so genannte Bundles, die eigentlich JAR-Dateien sind, installieren, deinstallieren, starten und stoppen. Aber es kann noch ein bisschen mehr, da es z.B. Dienste anbietet, die dynamisch in JAR-Dateien zur Laufzeit entdeckt werden können.

Oder siehe die Spezifikation für das Java-Modul-System .

44voto

Chris Punkte 14839

Wie wäre es mit dem JCL-Klassenlader-Framework ? Ich muss zugeben, dass ich es noch nicht benutzt habe, aber es sieht vielversprechend aus.

Beispiel für die Verwendung:

JarClassLoader jcl = new JarClassLoader();
jcl.add("myjar.jar"); // Load jar file  
jcl.add(new URL("http://myserver.com/myjar.jar")); // Load jar from a URL
jcl.add(new FileInputStream("myotherjar.jar")); // Load jar file from stream
jcl.add("myclassfolder/"); // Load class folder  
jcl.add("myjarlib/"); // Recursively load all jar files in the folder/sub-folder(s)

JclObjectFactory factory = JclObjectFactory.getInstance();
// Create object of loaded class  
Object obj = factory.create(jcl, "mypackage.MyClass");

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X