Comment dois-je charger les Jars dynamicment à l’exécution?

Pourquoi est-ce si difficile de faire cela en Java? Si vous voulez avoir un système de module quelconque, vous devez pouvoir charger les jars de manière dynamic. On m’a dit qu’il était possible de le faire en écrivant votre propre ClassLoader , mais c’est beaucoup de travail pour quelque chose qui devrait (dans mon esprit du moins) être aussi simple que d’appeler une méthode avec un fichier jar comme argument.

Des suggestions pour un code simple qui fait cela?

La raison pour laquelle c’est difficile, c’est la sécurité. Les chargeurs de classe sont censés être immuables; vous ne devriez pas pouvoir y append des classes au moment de l’exécution. Je suis en fait très surpris que cela fonctionne avec le classloader du système. Voici comment vous faites votre propre classloader enfant:

 URLClassLoader child = new URLClassLoader(myJar.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); 

Douloureux, mais ça y est.

La solution suivante est piratée, car elle utilise la reflection pour contourner l’encapsulation, mais elle fonctionne parfaitement:

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

Vous devriez regarder OSGi , par exemple implémenté dans la plate-forme Eclipse . C’est exactement ce que fait Vous pouvez installer, désinstaller, démarrer et arrêter des offres groupées, qui sont en fait des fichiers JAR. Mais il en fait un peu plus, car il offre par exemple des services qui peuvent être découverts dynamicment dans les fichiers JAR au moment de l’exécution.

Ou consultez la spécification du système de modules Java .

Qu’en est-il du cadre du chargeur de classes JCL ? Je dois admettre que je ne l’ai pas utilisé, mais cela semble prometteur.

Exemple d’utilisation:

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

Voici une version qui n’est pas obsolète. J’ai modifié l’original pour supprimer la fonctionnalité obsolète.

 /************************************************************************************************** * Copyright (c) 2004, Federal University of So Carlos * * * * All rights reserved. * * * * Redissortingbution and use in source and binary forms, with or without modification, are permitted * * provided that the following conditions are met: * * * * * Redissortingbutions of source code must retain the above copyright notice, this list of * * conditions and the following disclaimer. * * * Redissortingbutions 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 dissortingbution. * * * Neither the name of the Federal University of So Carlos nor the names of its * * * consortingbutors 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 sa Ssortingng pointing to the file * @throws IOException */ public static void addFile(Ssortingng 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(Ssortingng args[]) throws IOException, SecurityException, ClassNotFoundException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException{ addFile("C:\\dynamicloading.jar"); Constructor cs = ClassLoader.getSystemClassLoader().loadClass("test.DymamicLoadingTest").getConstructor(Ssortingng.class); DymamicLoadingTest instance = (DymamicLoadingTest)cs.newInstance(); instance.test(); } } 

Le meilleur que j’ai trouvé est org.apache.xbean.classloader.JarFileClassLoader qui fait partie du projet XBean .

Voici une méthode courte que j’ai utilisée par le passé pour créer un chargeur de classe à partir de tous les fichiers lib dans un répertoire spécifique

 public void initialize(Ssortingng libDir) throws Exception { File dependencyDirectory = new File(libDir); File[] files = dependencyDirectory.listFiles(); ArrayList urls = new ArrayList(); 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()); } 

Ensuite, pour utiliser le chargeur de classes, faites simplement:

 classLoader.loadClass(name); 

Les réponses avec URLClassLoader ne fonctionnent plus dans Java 9, car les chargeurs de classes utilisés ont changé.

Pour append au chargeur de classe système, vous pouvez utiliser l’API Instrumentation via un agent.

Créez une classe d’agent:

 package ClassPathAgent; import java.io.IOException; import java.lang.instrument.Instrumentation; import java.util.jar.JarFile; public class ClassPathAgent { public static void agentmain(Ssortingng args, Instrumentation instrumentation) throws IOException { instrumentation.appendToSystemClassLoaderSearch(new JarFile(args)); } } 

Ajoutez META-INF / MANIFEST.MF et placez-le dans un fichier JAR avec la classe d’agent:

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

Exécutez l’agent. Cela utilise la bibliothèque byte-buddy-agent pour append l’agent à la machine virtuelle Java en cours d’exécution:

 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, Ssortingng.valueOf(ProcessHandle.current().pid()), jarFile.getPath()); } } 

Si vous travaillez sur Android, le code suivant fonctionne:

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

La solution proposée par jodonnell est bonne mais devrait être un peu améliorée. J’ai utilisé ce post pour développer mon application avec succès.

Atsortingbuer le thread en cours

Tout d’abord, nous devons append

 Thread.currentThread().setContextClassLoader(classLoader); 

ou vous ne pourrez pas charger de ressource (telle que spring / context.xml) stockée dans le fichier jar.

N’incluez pas

vos pots dans le chargeur de classe parent ou vous ne pourrez pas comprendre qui charge quoi.

voir aussi Problème de rechargement d’un jar à l’aide d’URLClassLoader

Cependant, le cadre OSGi rest le meilleur moyen.

s’il vous plaît jeter un oeil à ce projet que j’ai commencé: proxy-object lib

Cette lib va ​​charger jar du système de fichiers ou de tout autre emplacement. Il dédiera un chargeur de classe au jar pour s’assurer qu’il n’ya pas de conflits de bibliothèque. Les utilisateurs pourront créer n’importe quel object à partir du fichier jar chargé et appeler n’importe quelle méthode. Cette lib a été conçue pour charger des fichiers JAR compilés en Java 8 à partir de la base de code prenant en charge Java 7.

Pour créer un object:

 ProxyCallerInterface object = ObjectBuilder.builder() .setPackageName("org.my.package") .setClassName("MyClass") .setVersionInfo(newVersionInfo("myLib", "2.0")) .build(); object.call("objectMethod"); 

ObjectBuilder prend en charge les méthodes d’usine, appelant des fonctions statiques et rappelle les implémentations d’interface. Je posterai plus d’exemples sur la page readme.

Je trouve personnellement que java.util.ServiceLoader fait très bien le travail. Vous pouvez obtenir un exemple ici .