Wie lade ich JAR-Dateien dynamisch zur Laufzeit?

308

Warum ist das in Java so schwierig? Wenn Sie ein Modulsystem haben möchten, müssen Sie in der Lage sein, JAR-Dateien dynamisch zu laden. Mir wurde gesagt, dass es eine Möglichkeit gibt, dies zu tun, indem Sie Ihre eigenen schreiben ClassLoader, aber das ist eine Menge Arbeit für etwas, das (zumindest in meinen Augen) so einfach sein sollte wie das Aufrufen einer Methode mit einer JAR-Datei als Argument.

Irgendwelche Vorschläge für einfachen Code, der dies tut?

Allain Lalonde
quelle
4
Ich möchte dasselbe tun, aber das geladene Glas in einer Umgebung mit mehr Sandbox ausführen (aus Sicherheitsgründen natürlich). Zum Beispiel möchte ich den gesamten Netzwerk- und Dateisystemzugriff blockieren.
Jus12

Antworten:

253

Der Grund, warum es schwierig ist, ist die Sicherheit. Klassenlader sollen unveränderlich sein; Sie sollten nicht in der Lage sein, zur Laufzeit wohl oder übel Klassen hinzuzufügen. Ich bin eigentlich sehr überrascht, dass das mit dem System Classloader funktioniert. So machen Sie Ihren eigenen Klassenlader:

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 da ist es.

Jodonnell
quelle
16
Das einzige Problem bei diesem Ansatz ist, dass Sie wissen müssen, welche Klassen sich in welchen Gläsern befinden. Im Gegensatz dazu, nur ein Verzeichnis von Jars zu laden und dann Klassen zu instanziieren. Ich verstehe es falsch?
Allain Lalonde
10
Diese Methode funktioniert hervorragend, wenn sie in meiner IDE ausgeführt wird. Wenn ich jedoch meine JAR erstelle, wird beim Aufrufen von Class.forName () eine ClassNotFoundException angezeigt.
Darrickc
29
Mit diesem Ansatz müssen Sie sicherstellen, dass Sie diese Lademethode nicht mehr als einmal für jede Klasse aufrufen. Da Sie für jede Ladeoperation einen neuen Klassenlader erstellen, kann er nicht wissen, ob die Klasse bereits zuvor geladen wurde. Dies kann schlimme Folgen haben. Zum Beispiel funktionieren Singletons nicht, weil die Klasse mehrmals geladen wurde und die statischen Felder daher mehrmals vorhanden sind.
Eduard Wirch
8
Funktioniert. Auch bei Abhängigkeiten zu anderen Klassen im Glas. Die erste Zeile war unvollständig. Ich habe URLClassLoader child = new URLClassLoader (new URL[] {new URL("file://./my.jar")}, Main.class.getClassLoader());angenommen, dass die JAR-Datei aufgerufen wird my.jarund sich im selben Verzeichnis befindet.
Kiefer
4
Vergessen Sie nicht, URL url = file.toURI (). ToURL ();
Johnstosh
139

Die folgende Lösung ist hackisch, da sie Reflexion 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);
Allain Lalonde
quelle
40
Bei all den Aktivitäten zu dieser Antwort frage ich mich, wie viele Hacks wir in der Produktion in verschiedenen Systemen ausführen. Ich bin nicht sicher, ob ich die Antwort wissen will
Andrei Savu
6
Funktioniert nicht so gut, wenn der Systemklassenlader etwas anderes als ein URLClassLoader ist ...
Gus
6
Java 9+ warnt davor, dass die URLClassLoader.class.getDeclaredMethod("addURL", URL.class)Verwendung von Reflektion illegal ist und in Zukunft fehlschlagen wird.
Charlweed
1
Haben Sie eine Idee, wie Sie diesen Code aktualisieren können, um mit Java 9+ zu arbeiten?
FiReTiTi
1
@FiReTiTi Ja !!
Mordechai
51

Sie sollten sich OSGi ansehen , z. B. in der Eclipse-Plattform implementiert . Es macht genau das. Sie können sogenannte Bundles installieren, deinstallieren, starten und stoppen, bei denen es sich effektiv um JAR-Dateien handelt. Aber es macht ein bisschen mehr, da es z. B. Dienste bietet, die zur Laufzeit dynamisch in JAR-Dateien erkannt werden können.

Oder lesen Sie die Spezifikation für das Java Module System .

Martin Klinke
quelle
41

Wie wäre es mit dem JCL Class Loader Framework ? Ich muss zugeben, ich habe es nicht benutzt, aber es sieht vielversprechend aus.

Anwendungsbeispiel:

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");
Chris
quelle
9
Es ist auch fehlerhaft und es fehlen einige wichtige Implementierungen, z. B. findResources (...). Seien Sie bereit, wundervolle Nächte damit zu verbringen, zu untersuchen, warum bestimmte Dinge nicht funktionieren =)
Sergey Karpushin
Ich frage mich immer noch, ob die Behauptungen von @ SergeyKarpushin immer noch vorhanden sind, da das Projekt im Laufe der Zeit auf die zweite Hauptversion aktualisiert wurde. Möchte Erfahrung hören.
Erdin Eray
2
@ErdinEray, es ist eine sehr gute Frage, die ich mir auch stelle, da wir "gezwungen" waren, zu OpenJDK zu wechseln. Ich arbeite immer noch an Java-Projekten und ich habe keine Beweise dafür, dass Open JDK Sie heutzutage scheitern wird (ich hatte damals allerdings ein Problem). Ich denke, ich ziehe meinen Anspruch zurück, bis ich auf etwas anderes stoße.
Sergey Karpushin
20

Hier ist eine Version, die nicht veraltet ist. Ich habe das Original geändert, um die veraltete Funktionalität zu entfernen.

/**************************************************************************************************
 * Copyright (c) 2004, Federal University of So Carlos                                           *
 *                                                                                                *
 * All rights reserved.                                                                           *
 *                                                                                                *
 * Redistribution and use in source and binary forms, with or without modification, are permitted *
 * provided that the following conditions are met:                                                *
 *                                                                                                *
 *     * Redistributions of source code must retain the above copyright notice, this list of      *
 *       conditions and the following disclaimer.                                                 *
 *     * Redistributions in binary form must reproduce the above copyright notice, this list of   *
 *     * conditions and the following disclaimer in the documentation and/or other materials      *
 *     * provided with the distribution.                                                          *
 *     * Neither the name of the Federal University of So Carlos nor the names of its            *
 *     * contributors may be used to endorse or promote products derived from this software       *
 *     * without specific prior written permission.                                               *
 *                                                                                                *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS                            *
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT                              *
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR                          *
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR                  *
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,                          *
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,                            *
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR                             *
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF                         *
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING                           *
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS                             *
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.                                   *
 **************************************************************************************************/
/*
 * Created on Oct 6, 2004
 */
package tools;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;

/**
 * Useful class for dynamically changing the classpath, adding classes during runtime. 
 */
public class ClasspathHacker {
    /**
     * Parameters of the method to add an URL to the System classes. 
     */
    private static final Class<?>[] parameters = new Class[]{URL.class};

    /**
     * Adds a file to the classpath.
     * @param s a String pointing to the file
     * @throws IOException
     */
    public static void addFile(String s) throws IOException {
        File f = new File(s);
        addFile(f);
    }

    /**
     * Adds a file to the classpath
     * @param f the file to be added
     * @throws IOException
     */
    public static void addFile(File f) throws IOException {
        addURL(f.toURI().toURL());
    }

    /**
     * Adds the content pointed by the URL to the classpath.
     * @param u the URL pointing to the content to be added
     * @throws IOException
     */
    public static void addURL(URL u) throws IOException {
        URLClassLoader sysloader = (URLClassLoader)ClassLoader.getSystemClassLoader();
        Class<?> sysclass = URLClassLoader.class;
        try {
            Method method = sysclass.getDeclaredMethod("addURL",parameters);
            method.setAccessible(true);
            method.invoke(sysloader,new Object[]{ u }); 
        } catch (Throwable t) {
            t.printStackTrace();
            throw new IOException("Error, could not add URL to system classloader");
        }        
    }

    public static void main(String args[]) throws IOException, SecurityException, ClassNotFoundException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException{
        addFile("C:\\dynamicloading.jar");
        Constructor<?> cs = ClassLoader.getSystemClassLoader().loadClass("test.DymamicLoadingTest").getConstructor(String.class);
        DymamicLoadingTest instance = (DymamicLoadingTest)cs.newInstance();
        instance.test();
    }
}
Jonathan Nadeau
quelle
19
Ich hasse es, einen alten Thread zu stoßen, aber ich möchte darauf hinweisen, dass alle Inhalte auf dem Stackoverflow CC-lizenziert sind. Ihre Urheberrechtserklärung ist effektiv unwirksam. stackoverflow.com/faq#editing
Huckle
43
Äh. Technisch gesehen ist der Originalinhalt CC-lizenziert. Wenn Sie hier jedoch urheberrechtlich geschützten Inhalt veröffentlichen, wird die Tatsache, dass der Inhalt urheberrechtlich geschützt ist, nicht beseitigt. Wenn ich ein Bild von Mickey Mouse poste, wird es nicht CC-lizenziert. Also füge ich die Copyright-Erklärung zurück.
Jason S
19

Während die meisten hier aufgeführten Lösungen entweder schwer zu konfigurierende Hacks (vor JDK 9) sind (Agenten) oder einfach nicht mehr funktionieren (nach JDK 9), finde ich es wirklich schockierend, dass niemand eine klar dokumentierte Methode erwähnt hat .

Sie können einen benutzerdefinierten Systemklassenlader erstellen und dann tun, was Sie möchten. Keine Reflexion erforderlich und alle Klassen verwenden denselben Klassenladeprogramm.

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

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

Der Klassenladeprogramm muss über einen Konstruktor verfügen, der einen Klassenladeprogramm akzeptiert, das als übergeordnetes Element festgelegt werden muss. Der Konstruktor wird beim JVM-Start aufgerufen und der echte Systemklassenlader wird übergeben, die Hauptklasse wird vom benutzerdefinierten Lader geladen.

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

Schauen Sie sich diese Implementierung für einen sorgfältig ausgearbeiteten Classloader an. Bitte beachten Sie, dass Sie die add()Methode in public ändern können .

Mordechai
quelle
Danke - das ist wirklich hilfreich! Alle anderen Verweise auf Webverwendungsmethoden für JDK 8 oder früher - was mehrere Probleme hat.
Vishal Biyani
15

Bei Java 9 geben die Antworten mit URLClassLoaderjetzt einen Fehler wie:

java.lang.ClassCastException: java.base/jdk.internal.loader.ClassLoaders$AppClassLoader cannot be cast to java.base/java.net.URLClassLoader

Dies liegt daran, dass sich die verwendeten Klassenlader geändert haben. Um den Systemklassenlader zu erweitern, können Sie stattdessen die Instrumentation- API über einen Agenten verwenden.

Erstellen Sie eine Agentenklasse:

package ClassPathAgent;

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

public class ClassPathAgent {
    public static void agentmain(String args, Instrumentation instrumentation) throws IOException {
        instrumentation.appendToSystemClassLoaderSearch(new JarFile(args));
    }
}

Fügen Sie META-INF / MANIFEST.MF hinzu und fügen Sie es in eine JAR-Datei mit der Agentenklasse ein:

Manifest-Version: 1.0
Agent-Class: ClassPathAgent.ClassPathAgent

Führen Sie den Agenten aus:

Dies verwendet die Byte-Buddy-Agent- Bibliothek, um den Agenten zur laufenden JVM hinzuzufügen:

import java.io.File;

import net.bytebuddy.agent.ByteBuddyAgent;

public class ClassPathUtil {
    private static File AGENT_JAR = new File("/path/to/agent.jar");

    public static void addJarToClassPath(File jarFile) {
        ByteBuddyAgent.attach(AGENT_JAR, String.valueOf(ProcessHandle.current().pid()), jarFile.getPath());
    }
}
fgb
quelle
9

Das Beste, was ich gefunden habe, ist org.apache.xbean.classloader.JarFileClassLoader, das Teil des XBean- Projekts ist.

Hier ist eine kurze Methode, die ich in der Vergangenheit verwendet habe, um einen Klassenlader aus allen lib-Dateien in einem bestimmten Verzeichnis zu erstellen

public void initialize(String libDir) throws Exception {
    File dependencyDirectory = new File(libDir);
    File[] files = dependencyDirectory.listFiles();
    ArrayList<URL> urls = new ArrayList<URL>();
    for (int i = 0; i < files.length; i++) {
        if (files[i].getName().endsWith(".jar")) {
        urls.add(files[i].toURL());
        //urls.add(files[i].toURI().toURL());
        }
    }
    classLoader = new JarFileClassLoader("Scheduler CL" + System.currentTimeMillis(), 
        urls.toArray(new URL[urls.size()]), 
        GFClassLoader.class.getClassLoader());
}

Um den Klassenlader zu verwenden, gehen Sie einfach wie folgt vor:

classLoader.loadClass(name);
Zeusoflightning125
quelle
Beachten Sie, dass das Projekt nicht sehr gut gepflegt zu sein scheint. Ihre Roadmap für die Zukunft enthält beispielsweise mehrere Veröffentlichungen für 2014.
Zero3
6

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");
Caner
quelle
6

Hier ist eine schnelle Problemumgehung für Allains Methode, um sie mit neueren Java-Versionen kompatibel zu machen:

ClassLoader classLoader = ClassLoader.getSystemClassLoader();
try {
    Method method = classLoader.getClass().getDeclaredMethod("addURL", URL.class);
    method.setAccessible(true);
    method.invoke(classLoader, new File(jarPath).toURI().toURL());
} catch (NoSuchMethodException e) {
    Method method = classLoader.getClass()
            .getDeclaredMethod("appendToClassPathForInstrumentation", String.class);
    method.setAccessible(true);
    method.invoke(classLoader, jarPath);
}

Beachten Sie, dass es auf dem Wissen über die interne Implementierung einer bestimmten JVM beruht. Daher ist es nicht ideal und keine universelle Lösung. Es ist jedoch eine schnelle und einfache Problemumgehung, wenn Sie wissen, dass Sie Standard-OpenJDK oder Oracle JVM verwenden werden. Es könnte auch irgendwann in der Zukunft kaputt gehen, wenn eine neue JVM-Version veröffentlicht wird. Sie müssen dies also berücksichtigen.

Anton Tananaev
quelle
Mit Java 11.0.2 bekomme ich:Exception in thread "main" java.lang.reflect.InaccessibleObjectException: Unable to make void jdk.internal.loader.ClassLoaders$AppClassLoader.appendToClassPathForInstrumentation(java.lang.String) accessible: module java.base does not "opens jdk.internal.loader" to unnamed module @18ef96
Richard Żak
Funktioniert mit Java 8 EE in einer Anwendungsserverumgebung.
Januar
4

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

Weisen Sie den aktuellen Thread zu

Zuerst müssen wir hinzufügen

Thread.currentThread().setContextClassLoader(classLoader);

oder Sie können keine in das Glas gespeicherte Ressource (z. B. spring / context.xml) laden.

Nicht einschließen

Ihre Gläser in den übergeordneten Klassenlader oder Sie werden nicht verstehen können, wer was lädt.

Siehe auch Problem beim erneuten Laden eines JAR mit URLClassLoader

Das OSGi-Framework bleibt jedoch der beste Weg.

venergiac
quelle
2
Ihre Antwort erscheint etwas verwirrend und eignet sich möglicherweise eher als Kommentar zur Antwort von Jodonnell, wenn es sich lediglich um eine einfache Verbesserung handelt.
Zero3
4

Eine andere Version der hackischen Lösung von Allain, die auch auf 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});

In JDK 11 werden einige Verwerfungswarnungen ausgegeben, sie dienen jedoch als vorübergehende Lösung für diejenigen, die die Allain-Lösung in JDK 11 verwenden.

czdepski
quelle
Kann ich auch Glas entfernen?
user7294900
3

Eine weitere funktionierende Lösung mit Instrumentation, die für mich funktioniert. Es hat den Vorteil, dass die Suche nach Klassenladeprogrammen geändert wird, wodurch Probleme bei der Sichtbarkeit von Klassen für abhängige Klassen vermieden werden:

Erstellen Sie eine Agentenklasse

In diesem Beispiel muss es sich in demselben JAR befinden, das von der 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 MANIFEST.MF

Hinzufügen des Verweises zum Agenten:

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

Ich benutze tatsächlich Netbeans, daher hilft dieser Beitrag beim Ändern der manifest.mf

Laufen

Das Launcher-Agent-Classwird nur von JDK 9+ unterstützt und ist für das Laden des Agenten verantwortlich, ohne ihn explizit in der Befehlszeile zu definieren:

 java -jar <your jar>

Die Funktionsweise von JDK 6+ definiert das -javaagentArgument:

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

Hinzufügen eines neuen Glases zur Laufzeit

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

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

Ich habe in der Dokumentation keine Probleme damit gefunden.

czdepski
quelle
Aus irgendeinem Grund erhalte ich bei Verwendung dieser Lösung "Ausnahme im Thread" main "java.lang.ClassNotFoundException: agent.Agent". Ich habe die "Agent" -Klasse in meine Hauptanwendung "Krieg"
gepackt
3

Falls jemand in Zukunft danach sucht, funktioniert dies für mich mit OpenJDK 13.0.2.

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

In diesem Code habe ich bereits ein Objekt namens pack, das einige Metadaten zu der Klasse enthält, die ich laden möchte. Die Methode getObjectFile () gibt den Speicherort der Klassendatei für die Klasse zurück. Die Methode getObjectRootPath () gibt den Pfad zum Verzeichnis bin / zurück, das die Klassendateien enthält, die die Klasse enthalten, die ich instanziieren möchte. 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 habe die Maven-Abhängigkeit: org.xeustechnologies: jcl-core: 2.8 verwendet, um dies zuvor zu tun, aber nachdem ich JDK 1.8 überschritten habe, ist sie manchmal eingefroren und hat nie wieder "auf Referenzen gewartet" bei Reference :: waitForReferencePendingList ().

Ich behalte auch eine Karte mit Klassenladeprogrammen, damit diese wiederverwendet werden können, wenn sich die Klasse, die ich instanziieren möchte, im selben Modul befindet wie eine Klasse, die ich bereits instanziiert habe, was ich empfehlen würde.

ZGorlock
quelle
2

Bitte schauen Sie sich dieses Projekt an, das ich gestartet habe: proxy-object lib

Diese Bibliothek lädt JAR aus dem Dateisystem oder einem anderen Speicherort. Es wird ein Klassenladeprogramm für das JAR reserviert, um sicherzustellen, dass keine Bibliothekskonflikte vorliegen. Benutzer können jedes Objekt aus dem geladenen JAR erstellen und eine beliebige Methode darauf aufrufen. Diese Bibliothek wurde entwickelt, um in Java 8 kompilierte Jars aus der Codebasis zu laden, die Java 7 unterstützt.

So erstellen Sie ein Objekt:

    File libDir = new File("path/to/jar");

    ProxyCallerInterface caller = ObjectBuilder.builder()
            .setClassName("net.proxy.lib.test.LibClass")
            .setArtifact(DirArtifact.builder()
                    .withClazz(ObjectBuilderTest.class)
                    .withVersionInfo(newVersionInfo(libDir))
                    .build())
            .build();
    String version = caller.call("getLibVersion").asString();

ObjectBuilder unterstützt Factory-Methoden, ruft statische Funktionen auf und ruft Schnittstellenimplementierungen zurück. Ich werde weitere Beispiele auf der Readme-Seite veröffentlichen.

Aleksey
quelle
2

Dies kann eine späte Antwort sein. Ich kann dies wie folgt tun (ein einfaches Beispiel für fastutil-8.2.2.jar), indem ich die Klasse jhplot.Web von DataMelt ( http://jwork.org/dmelt ) verwende.

import jhplot.Web;
Web.load("http://central.maven.org/maven2/it/unimi/dsi/fastutil/8.2.2/fastutil-8.2.2.jar"); // now you can start using this library

Gemäß der Dokumentation wird diese Datei in "lib / user" heruntergeladen und dann dynamisch geladen, sodass Sie sofort Klassen aus dieser JAR-Datei im selben Programm verwenden können.

steve212
quelle
1

Ich musste zur Laufzeit eine JAR-Datei für Java 8 und Java 9+ laden (die obigen Kommentare funktionieren nicht für beide Versionen). Hier ist die Methode, um dies zu tun (mit Spring Boot 1.5.2, wenn es sich darauf bezieht).

public static synchronized void loadLibrary(java.io.File jar) {
    try {            
        java.net.URL url = jar.toURI().toURL();
        java.lang.reflect.Method method = java.net.URLClassLoader.class.getDeclaredMethod("addURL", new Class[]{java.net.URL.class});
        method.setAccessible(true); /*promote the method to public access*/
        method.invoke(Thread.currentThread().getContextClassLoader(), new Object[]{url});
    } catch (Exception ex) {
        throw new RuntimeException("Cannot load library from jar file '" + jar.getAbsolutePath() + "'. Reason: " + ex.getMessage());
    }
}
Bằng Rikimaru
quelle
-2

Ich persönlich finde, dass java.util.ServiceLoader den Job ziemlich gut macht. Ein Beispiel finden Sie hier .

tanyehzheng
quelle
11
ServiceLoader fügt JAR-Dateien zur Laufzeit nicht dynamisch hinzu. JAR-Dateien müssen sich zuvor im Klassenpfad befinden.
Angelcervera