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?

6voto

czdepski Punkte 196

Eine weitere funktionierende Lösung mit Instrumentation, die bei mir funktioniert. Sie hat den Vorteil, dass sie die Suche des Klassenladers modifiziert und Probleme mit der Sichtbarkeit von Klassen für abhängige Klassen vermeidet:

Erstellen einer Agentenklasse

In diesem Beispiel muss es sich um dasselbe jar handeln, das über die Befehlszeile aufgerufen wird:

package agent;

import java.io.IOException;
import java.lang.instrument.Instrumentation;
import java.util.jar.JarFile;

public class Agent {
   public static Instrumentation instrumentation;

   public static void premain(String args, Instrumentation instrumentation) {
      Agent.instrumentation = instrumentation;
   }

   public static void agentmain(String args, Instrumentation instrumentation) {
      Agent.instrumentation = instrumentation;
   }

   public static void appendJarFile(JarFile file) throws IOException {
      if (instrumentation != null) {
         instrumentation.appendToSystemClassLoaderSearch(file);
      }
   }
}

Ändern Sie die Datei MANIFEST.MF

Hinzufügen des Verweises auf den Agenten:

Launcher-Agent-Class: agent.Agent
Agent-Class: agent.Agent
Premain-Class: agent.Agent

Ich verwende eigentlich Netbeans, also diese Stelle Hilfe bei der Änderung der manifest.mf

Laufen

En Launcher-Agent-Class wird nur von JDK 9+ unterstützt und ist für das Laden des Agenten verantwortlich, ohne dass er explizit auf der Kommandozeile definiert wird:

 java -jar <your jar>

Die Art und Weise, wie das unter JDK 6+ funktioniert, ist die Definition der -javaagent Argument:

java -javaagent:<your jar> -jar <your jar>

Hinzufügen neuer Jar zur Laufzeit

Mit dem folgenden Befehl können Sie dann je nach Bedarf jar hinzufügen:

Agent.appendJarFile(new JarFile(<your file>));

Ich habe keine Probleme bei der Verwendung dieser Dokumentation festgestellt.

6voto

Caner Punkte 53228

Wenn Sie mit Android arbeiten, funktioniert der folgende Code:

String jarFile = "path/to/jarfile.jar";
DexClassLoader classLoader = new DexClassLoader(jarFile, "/data/data/" + context.getPackageName() + "/", null, getClass().getClassLoader());
Class<?> myClass = classLoader.loadClass("MyClass");

5voto

czdepski Punkte 196

Eine andere Version der hackish Lösung von Allain, die auch unter JDK 11 funktioniert:

File file = ...
URL url = file.toURI().toURL();
URLClassLoader sysLoader = new URLClassLoader(new URL[0]);

Method sysMethod = URLClassLoader.class.getDeclaredMethod("addURL", new Class[]{URL.class});
sysMethod.setAccessible(true);
sysMethod.invoke(sysLoader, new Object[]{url});

Unter JDK 11 gibt es einige Veralterungswarnungen, aber es dient als vorübergehende Lösung für diejenigen, die die Allain-Lösung unter JDK 11 verwenden.

4voto

venergiac Punkte 6971

Die von jodonnell vorgeschlagene Lösung ist gut, sollte aber noch ein wenig verbessert werden. Ich habe diesen Beitrag verwendet, um meine Anwendung mit Erfolg zu entwickeln.

Den aktuellen Thread zuweisen

Erstens müssen wir hinzufügen

Thread.currentThread().setContextClassLoader(classLoader);

sonst können Sie die in der Jar-Datei gespeicherten Ressourcen (z. B. spring/context.xml) nicht laden.

Nicht enthalten sind

Ihre Jars in den Parent Class Loader einbinden, sonst können Sie nicht nachvollziehen, wer was lädt.

siehe auch Problem beim Nachladen eines Jars mit URLClassLoader

Das OSGi-Framework bleibt jedoch der beste Weg.

4voto

ZGorlock Punkte 506

Falls jemand in Zukunft danach sucht, dieser Weg funktioniert bei mir mit OpenJDK 13.0.2.

Ich habe viele Klassen, die ich dynamisch zur Laufzeit instanziieren muss, jede möglicherweise mit einem anderen Klassenpfad.

In diesem Code habe ich bereits ein Objekt namens pack, das einige Metadaten über die Klasse enthält, die ich zu laden versuche. Die Methode getObjectFile() gibt den Speicherort der Klassendatei für die Klasse zurück. Die Methode getObjectRootPath() gibt den Pfad zum bin/-Verzeichnis zurück, das die Klassendateien enthält, die die Klasse, die ich zu instanzieren versuche, beinhalten. Die Methode getLibPath() gibt den Pfad zu einem Verzeichnis zurück, das die jar-Dateien enthält, die den Klassenpfad für das Modul bilden, zu dem die Klasse gehört.

File object = new File(pack.getObjectFile()).getAbsoluteFile();
Object packObject;
try {
    URLClassLoader classloader;

    List<URL> classpath = new ArrayList<>();
    classpath.add(new File(pack.getObjectRootPath()).toURI().toURL());
    for (File jar : FileUtils.listFiles(new File(pack.getLibPath()), new String[] {"jar"}, true)) {
        classpath.add(jar.toURI().toURL());
    }
    classloader = new URLClassLoader(classpath.toArray(new URL[] {}));

    Class<?> clazz = classloader.loadClass(object.getName());
    packObject = clazz.getDeclaredConstructor().newInstance();

} catch (Exception e) {
    e.printStackTrace();
    throw e;
}
return packObject;

Ich war mit der Maven-Abhängigkeit: org.xeustechnologies:jcl-core:2.8, dies zu tun, bevor, aber nach dem Verschieben Vergangenheit JDK 1.8, es manchmal eingefroren und nie zurückgegeben werden stecken "Warten auf Referenzen" bei Reference::waitForReferencePendingList().

Ich führe auch eine Karte der Klassenlader, damit sie wiederverwendet werden können, wenn die Klasse, die ich zu instanziieren versuche, im selben Modul ist wie eine Klasse, die ich bereits instanziiert habe, was ich empfehlen würde.

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