Jak dynamicznie ładować pliki JAR w Runtime?

308

Dlaczego tak trudno jest to zrobić w Javie? Jeśli chcesz mieć dowolny system modułowy, musisz mieć możliwość dynamicznego ładowania plików JAR. Powiedziano mi, że można to zrobić, pisząc własne ClassLoader, ale jest to dużo pracy dla czegoś, co powinno (przynajmniej moim zdaniem) być tak proste, jak wywołanie metody z plikiem JAR jako argumentem.

Wszelkie sugestie dotyczące prostego kodu, który to robi?

Allain Lalonde
źródło
4
Chcę zrobić to samo, ale uruchomić załadowany słoik w środowisku z piaskownicą (oczywiście ze względów bezpieczeństwa). Na przykład chcę zablokować cały dostęp do sieci i systemu plików.
Jus12

Odpowiedzi:

253

Przyczyną tego jest bezpieczeństwo. Programy ładujące klasy mają być niezmienne; nie powinieneś być w stanie nie chcąc dodawać do niego klas w czasie wykonywania. Jestem bardzo zaskoczony, że działa z systemowym modułem ładującym. Oto, jak to robisz, tworząc własny moduł ładujący dziecko:

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);

Bolesne, ale jest.

jodonnell
źródło
16
Jedynym problemem związanym z tym podejściem jest to, że musisz wiedzieć, jakie klasy są w jakich słoikach. W przeciwieństwie do zwykłego ładowania katalogu słoików i tworzenia instancji klas. Nie rozumiem tego?
Allain Lalonde,
10
Ta metoda działa świetnie podczas uruchamiania w moim IDE, ale kiedy buduję mój plik JAR, otrzymuję wyjątek ClassNotFoundException podczas wywoływania Class.forName ().
darrickc
29
Korzystając z tego podejścia, musisz upewnić się, że nie wywołasz tej metody ładowania więcej niż raz dla każdej klasy. Ponieważ tworzysz nowy moduł ładujący klasy dla każdej operacji ładowania, nie może on wiedzieć, czy klasa została już wcześniej załadowana. Może to mieć złe konsekwencje. Na przykład singletony nie działają, ponieważ klasa została załadowana kilka razy, więc pola statyczne istnieją kilka razy.
Eduard Wirch
8
Pracuje. Nawet z zależnościami od innych klas w słoiku. Pierwsza linia była niekompletna. Użyłem URLClassLoader child = new URLClassLoader (new URL[] {new URL("file://./my.jar")}, Main.class.getClassLoader());założenia, że ​​plik jar jest wywoływany my.jari znajduje się w tym samym katalogu.
szczęka
4
Nie zapomnij o URL url = file.toURI (). ToURL ();
johnstosh
139

Poniższe rozwiązanie jest hackerskie, ponieważ wykorzystuje odbicie do obejścia enkapsulacji, ale działa bezbłędnie:

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
źródło
40
Cała aktywność związana z tą odpowiedzią sprawia, że ​​zastanawiam się, ile hacków produkujemy w różnych systemach. Nie jestem pewien, czy chcę znać odpowiedź
Andrei Savu
6
Nie działa tak dobrze, jeśli moduł ładujący klasy systemowej jest czymś innym niż URLClassLoader ...
Gus
6
Java 9+ ostrzega przed URLClassLoader.class.getDeclaredMethod("addURL", URL.class)nielegalnym użyciem refleksji i zawiedzie w przyszłości.
Charlweed
1
Masz pomysł, jak zaktualizować ten kod do pracy z Javą 9+?
FiReTiTi
1
@FiReTiTi Tak !!
Mordechai
51

Powinieneś spojrzeć na OSGi , np. Zaimplementowane w platformie Eclipse . Robi dokładnie to. Możesz instalować, odinstalowywać, uruchamiać i zatrzymywać tak zwane pakiety, które są faktycznie plikami JAR. Ale robi to nieco więcej, ponieważ oferuje np. Usługi, które można dynamicznie wykrywać w plikach JAR w czasie wykonywania.

Lub zobacz specyfikację Java Module System .

Martin Klinke
źródło
41

Co powiesz na framework ładujący klasy JCL ? Muszę przyznać, że go nie użyłem, ale wygląda obiecująco.

Przykład użycia:

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
źródło
9
Jest również błędny i brakuje niektórych ważnych implementacji, np. FindResources (...). Przygotuj się na cudowne noce, badając, dlaczego pewne rzeczy nie działają =)
Sergey Karpushin
Nadal zastanawiam się, twierdzenia @ SergeyKarpushin są nadal obecne, ponieważ projekt został z czasem zaktualizowany do drugiej głównej wersji. Chciałbym usłyszeć doświadczenie.
Erdin Eray
2
@ErdinEray, to bardzo dobre pytanie, które zadaję sobie również, ponieważ zostaliśmy „zmuszeni” do przejścia na OpenJDK. Nadal pracuję nad projektami Java i nie mam żadnych dowodów na to, że Open JDK Cię teraz zawiedzie (chociaż wtedy miałem problem). Chyba wycofam swoje roszczenie, dopóki nie wpadnę na coś innego.
Sergey Karpushin,
20

Oto wersja, która nie jest przestarzała. Zmodyfikowałem oryginał, aby usunąć przestarzałą funkcjonalność.

/**************************************************************************************************
 * 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
źródło
19
Nienawidzę podbijać starego wątku, ale chciałbym zwrócić uwagę, że cała zawartość Stackoverflow ma licencję CC. Twoje oświadczenie o prawach autorskich jest skutecznie nieskuteczne. stackoverflow.com/faq#editing
Huckle
43
Um. Technicznie rzecz biorąc, oryginalna treść jest objęta licencją CC, ale jeśli opublikujesz tutaj treść chronioną prawem autorskim, nie usuwa to faktu, że treść jest chroniona prawem autorskim. Jeśli opublikuję zdjęcie Myszki Miki, nie będzie to miało licencji CC. Więc dodam ponownie oświadczenie o prawach autorskich.
Jason S,
19

Podczas gdy większość wymienionych tutaj rozwiązań to albo hacki (przed JDK 9) trudne do skonfigurowania (agenty), albo po prostu już nie działają (po JDK 9), uważam za naprawdę szokujące, że nikt nie wspomniał o jasno udokumentowanej metodzie .

Możesz utworzyć niestandardowy moduł ładujący klasy systemowej, a następnie możesz robić, co chcesz. Nie wymaga refleksji, a wszystkie klasy korzystają z tego samego modułu ładującego klasy.

Podczas uruchamiania maszyny JVM dodaj tę flagę:

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

Moduł ładujący klasy musi mieć konstruktor akceptujący moduł ładujący, który musi być ustawiony jako jego element nadrzędny. Konstruktor zostanie wywołany podczas uruchamiania JVM i przekaże prawdziwy system ładujący klasy, klasa główna zostanie załadowana przez moduł ładujący niestandardowy.

Aby dodać słoiki, po prostu zadzwoń ClassLoader.getSystemClassLoader()i rzuć je na swoją klasę.

Sprawdź tę implementację, aby uzyskać starannie spreparowany moduł ładujący klasy. Uwaga: możesz zmienić add()metodę na publiczną.

Mordechaj
źródło
Dziękuję - to jest naprawdę pomocne! Wszystkie inne odwołania na temat metod korzystania z Internetu dla JDK 8 lub wcześniejszych - które mają wiele problemów.
Vishal Biyani
15

W Javie 9 odpowiedzi z URLClassLoaderteraz dają błąd taki jak:

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

Jest tak, ponieważ zmieniły się używane ładowarki klas. Zamiast tego, aby dodać do modułu ładującego klasy systemowe, można użyć interfejsu API Instrumentacji za pośrednictwem agenta.

Utwórz klasę agenta:

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));
    }
}

Dodaj META-INF / MANIFEST.MF i umieść go w pliku JAR z klasą agenta:

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

Uruchom agenta:

Używa biblioteki byte-buddy-agent, aby dodać agenta do działającej maszyny JVM:

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
źródło
9

Najlepsze, co znalazłem, to org.apache.xbean.classloader.JarFileClassLoader, który jest częścią projektu XBean .

Oto krótka metoda, której użyłem w przeszłości, aby utworzyć moduł ładujący klasy ze wszystkich plików lib w określonym katalogu

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());
}

Następnie, aby użyć modułu ładującego klasy, po prostu wykonaj:

classLoader.loadClass(name);
Zeusoflightning125
źródło
Pamiętaj, że projekt nie wydaje się być bardzo dobrze utrzymany. Ich plan działania na przyszłość zawiera na przykład kilka wydań na 2014 rok.
Zero3,
6

Jeśli pracujesz na Androidzie, działa następujący kod:

String jarFile = "path/to/jarfile.jar";
DexClassLoader classLoader = new DexClassLoader(jarFile, "/data/data/" + context.getPackageName() + "/", null, getClass().getClassLoader());
Class<?> myClass = classLoader.loadClass("MyClass");
Caner
źródło
6

Oto krótkie obejście metody Allain w celu zapewnienia jej zgodności z nowszymi wersjami Javy:

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);
}

Pamiętaj, że opiera się on na wiedzy na temat wewnętrznej implementacji konkretnej maszyny JVM, więc nie jest idealna i nie jest rozwiązaniem uniwersalnym. Jest to jednak szybkie i łatwe obejście, jeśli wiesz, że zamierzasz używać standardowego OpenJDK lub Oracle JVM. Może się również zepsuć w przyszłości, gdy zostanie wydana nowa wersja JVM, dlatego należy o tym pamiętać.

Anton Tananaev
źródło
W Javie 11.0.2 otrzymuję: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
Współpracuje z Java 8 EE w środowisku serwera aplikacji.
stycznia
4

Rozwiązanie zaproponowane przez jodonnell jest dobre, ale należy je nieco ulepszyć. Wykorzystałem ten post do udanego opracowania mojej aplikacji.

Przypisz bieżący wątek

Najpierw musimy dodać

Thread.currentThread().setContextClassLoader(classLoader);

lub nie będziesz mógł załadować zasobu (takiego jak spring /ext.xml) przechowywanego w słoiku.

Nie zawiera

twoje słoiki do modułu ładującego klasy nadrzędnej, inaczej nie zrozumiesz, kto co ładuje.

zobacz także Problem z przeładowaniem słoika za pomocą URLClassLoader

Jednak środowisko OSGi pozostaje najlepszym sposobem.

wenergetyczny
źródło
2
Twoja odpowiedź wydaje się trochę myląca i może bardziej nadaje się jako komentarz do odpowiedzi Jodonnella, jeśli jest to zwykłe ulepszenie.
Zero3
4

Inna wersja hackish rozwiązania Allain, która działa również na JDK 11:

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});

W JDK 11 daje pewne ostrzeżenia o wycofaniu, ale służy jako rozwiązanie tymczasowe dla tych, którzy używają rozwiązania Allain w JDK 11.

czdepski
źródło
Czy mogę również usunąć słoik?
user7294900,
3

Kolejne działające rozwiązanie z wykorzystaniem Instrumentacji, które działa dla mnie. Ma tę zaletę, że modyfikuje wyszukiwanie modułu ładującego klasy, unikając problemów z widocznością klas dla klas zależnych:

Utwórz klasę agenta

W tym przykładzie musi znajdować się w tym samym słoju wywołanym z wiersza poleceń:

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);
      }
   }
}

Zmodyfikuj plik MANIFEST.MF

Dodanie odwołania do agenta:

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

Właściwie korzystam z Netbeans, więc ten post pomaga zmienić sposób manifestu.mf

Bieganie

Launcher-Agent-ClassJest obsługiwana tylko w JDK 9+ i jest odpowiedzialny za ładowanie agenta bez wyraźnego zdefiniowania go w wierszu poleceń:

 java -jar <your jar>

Sposób, w jaki działa JDK 6+, polega na zdefiniowaniu -javaagentargumentu:

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

Dodanie nowego słoika w Runtime

Następnie możesz dodać słoik, jeśli to konieczne, za pomocą następującego polecenia:

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

Nie znalazłem żadnych problemów z korzystaniem z tego w dokumentacji.

czdepski
źródło
Z jakiegoś powodu podczas korzystania z tego rozwiązania otrzymuję komunikat „Wyjątek w wątku” główny „java.lang.ClassNotFoundException: agent.Agent”.
Spakowałem
3

Jeśli ktoś będzie tego szukał w przyszłości, ten sposób działa dla mnie z OpenJDK 13.0.2.

Mam wiele klas, które muszę dynamicznie tworzyć w czasie wykonywania, każda potencjalnie z inną ścieżką klas.

W tym kodzie mam już obiekt o nazwie paczka, który przechowuje metadane dotyczące klasy, którą próbuję załadować. Metoda getObjectFile () zwraca położenie pliku klasy dla klasy. Metoda getObjectRootPath () zwraca ścieżkę do katalogu bin / zawierającego pliki klas zawierające klasę, którą próbuję utworzyć. Metoda getLibPath () zwraca ścieżkę do katalogu zawierającego pliki jar stanowiące ścieżkę klas dla modułu, którego klasa jest częścią.

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;

Używałem wcześniej zależności Maven: org.xeustechnologies: jcl-core: 2.8, ale po przejściu przez JDK 1.8 czasami się zawiesił i nigdy nie powrócił, utknął „czekając na referencje” w Reference :: waitForReferencePendingList ().

Przechowuję również mapę programów ładujących klasy, aby można było ich ponownie użyć, jeśli klasa, którą próbuję utworzyć, znajduje się w tym samym module co klasa, którą już utworzyłem, co poleciłbym.

ZGorlock
źródło
2

proszę spojrzeć na ten projekt, który rozpocząłem: proxy-object lib

Ta biblioteka załaduje jar z systemu plików lub dowolnej innej lokalizacji. Poświęci moduł ładujący dla słoika, aby upewnić się, że nie ma konfliktów bibliotek. Użytkownicy będą mogli utworzyć dowolny obiekt z załadowanego słoika i wywołać dowolną metodę na nim. Ta biblioteka została zaprojektowana do ładowania słoików skompilowanych w Javie 8 z bazy kodu obsługującej Javę 7.

Aby utworzyć obiekt:

    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 obsługuje metody fabryczne, wywoływanie funkcji statycznych i implementacje interfejsu zwrotnego. Będę publikować więcej przykładów na stronie readme.

Aleksey
źródło
2

Może to być spóźniona odpowiedź, mogę to zrobić w ten sposób (prosty przykład dla fastutil-8.2.2.jar) za pomocą klasy jhplot.Web z DataMelt ( http://jwork.org/dmelt )

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

Zgodnie z dokumentacją plik ten zostanie pobrany w „lib / user”, a następnie załadowany dynamicznie, dzięki czemu można natychmiast rozpocząć korzystanie z klas z tego pliku jar w tym samym programie.

steve212
źródło
1

Musiałem załadować plik jar w czasie wykonywania dla java 8 i java 9+ (powyższe komentarze nie działają dla obu tych wersji). Oto metoda, aby to zrobić (używając Spring Boot 1.5.2, jeśli może się to odnosić).

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
źródło
-2

Ja osobiście uważam, że java.util.ServiceLoader wykonuje to zadanie całkiem dobrze. Można dostać przykład tutaj .

tanyehzheng
źródło
11
ServiceLoader nie dodaje dynamicznie plików jar w czasie wykonywania. pliki jar muszą wcześniej znajdować się w ścieżce klasy.
angelcervera