Pourquoi est-il si difficile de faire cela en Java? Si vous voulez avoir n'importe quel type de système de modules, vous devez pouvoir charger dynamiquement les fichiers JAR. On me dit qu'il existe un moyen de le faire en écrivant le vôtre ClassLoader
, mais c'est beaucoup de travail pour quelque chose qui devrait (dans mon esprit au moins) être aussi simple que d'appeler une méthode avec un fichier JAR comme argument.
Des suggestions de code simple pour cela?
java
jar
classloader
Allain Lalonde
la source
la source
Réponses:
La raison pour laquelle c'est difficile est la sécurité. Les chargeurs de classe sont censés être immuables; vous ne devriez pas pouvoir y ajouter des classes au moment de l'exécution. Je suis en fait très surpris que cela fonctionne avec le chargeur de classe système. Voici comment procéder en créant votre propre chargeur de classe enfant:
Douloureux, mais ça y est.
la source
URLClassLoader child = new URLClassLoader (new URL[] {new URL("file://./my.jar")}, Main.class.getClassLoader());
supposant que le fichier jar est appelémy.jar
et se trouve dans le même répertoire.La solution suivante est piratée, car elle utilise la réflexion pour contourner l'encapsulation, mais elle fonctionne parfaitement:
la source
URLClassLoader.class.getDeclaredMethod("addURL", URL.class)
s'agit d'une utilisation illégale de la réflexion et qu'elle échouera à l'avenir.Vous devriez jeter un œil à OSGi , par exemple implémenté dans la plate-forme Eclipse . C'est exactement cela. Vous pouvez installer, désinstaller, démarrer et arrêter les soi-disant bundles, 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 dynamiquement dans les fichiers JAR lors de l'exécution.
Ou consultez les spécifications du système de modules Java .
la source
Que diriez-vous de l' infrastructure de chargeur de classe JCL ? Je dois admettre que je ne l'ai pas utilisé, mais cela semble prometteur.
Exemple d'utilisation:
la source
Voici une version qui n'est pas déconseillée. J'ai modifié l'original pour supprimer la fonctionnalité obsolète.
la source
Bien que la plupart des solutions répertoriées ici soient des hacks (avant JDK 9) difficiles à configurer (agents) ou ne fonctionnent tout simplement plus (post JDK 9), je trouve vraiment choquant que personne n'ait mentionné une méthode clairement documentée .
Vous pouvez créer un chargeur de classe système personnalisé, puis vous êtes libre de faire ce que vous voulez. Aucune réflexion requise et toutes les classes partagent le même chargeur de classe.
Au démarrage de la JVM, ajoutez cet indicateur:
Le chargeur de classe doit avoir un constructeur acceptant un chargeur de classe, qui doit être défini comme parent. Le constructeur sera appelé au démarrage de la machine virtuelle Java et le chargeur de classe système réel sera transmis, la classe principale sera chargée par le chargeur personnalisé.
Pour ajouter des pots, appelez-le
ClassLoader.getSystemClassLoader()
et lancez-le dans votre classe.Découvrez cette implémentation pour un chargeur de classe soigneusement conçu. Veuillez noter que vous pouvez changer la
add()
méthode en public.la source
Avec Java 9 , les réponses avec
URLClassLoader
donnent maintenant une erreur comme:En effet, les chargeurs de classe utilisés ont changé. Au lieu de cela, pour ajouter au chargeur de classe système, vous pouvez utiliser l' API Instrumentation via un agent.
Créez une classe d'agent:
Ajoutez META-INF / MANIFEST.MF et placez-le dans un fichier JAR avec la classe d'agent:
Exécutez l'agent:
Celui-ci utilise la bibliothèque byte-buddy-agent pour ajouter l'agent à la JVM en cours d'exécution:
la source
Le meilleur que j'ai trouvé est org.apache.xbean.classloader.JarFileClassLoader qui fait partie du projet XBean .
Voici une courte méthode que j'ai utilisée dans le passé, pour créer un chargeur de classe à partir de tous les fichiers lib dans un répertoire spécifique
Ensuite, pour utiliser le chargeur de classe, faites simplement:
la source
Si vous travaillez sur Android, le code suivant fonctionne:
la source
Voici une solution rapide pour la méthode d'Allain pour la rendre compatible avec les nouvelles versions de Java:
Notez qu'il repose sur la connaissance de l'implémentation interne de JVM spécifique, donc ce n'est pas idéal et ce n'est pas une solution universelle. Mais c'est une solution rapide et facile si vous savez que vous allez utiliser OpenJDK standard ou Oracle JVM. Il se peut également qu'il se brise à un moment donné à l'avenir lors de la sortie de la nouvelle version JVM, vous devez donc garder cela à l'esprit.
la source
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
La solution proposée par jodonnell est bonne mais devrait être un peu améliorée. J'ai utilisé ce message pour développer mon application avec succès.
Attribuer le thread actuel
Premièrement, nous devons ajouter
ou vous ne pourrez pas charger les ressources (telles que spring / context.xml) stockées dans le bocal.
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 bocal avec URLClassLoader
Cependant, le cadre OSGi reste le meilleur moyen.
la source
Une autre version de la solution hackeuse d'Allain, qui fonctionne également sur JDK 11:
Sur JDK 11, il donne des avertissements de dépréciation mais sert de solution temporaire à ceux qui utilisent la solution Allain sur JDK 11.
la source
Une autre solution de travail utilisant Instrumentation qui fonctionne pour moi. Il a l'avantage de modifier la recherche du chargeur de classe, en évitant les problèmes de visibilité des classes pour les classes dépendantes:
Créer une classe d'agent
Pour cet exemple, il doit être sur le même pot appelé par la ligne de commande:
Modifiez le MANIFEST.MF
Ajout de la référence à l'agent:
J'utilise en fait Netbeans, donc ce message aide à changer le manifest.mf
Fonctionnement
Le
Launcher-Agent-Class
n'est pris en charge que sur JDK 9+ et est responsable du chargement de l'agent sans le définir explicitement sur la ligne de commande:La façon dont cela fonctionne sur JDK 6+ définit l'
-javaagent
argument:Ajout d'un nouveau pot au moment de l'exécution
Vous pouvez ensuite ajouter le pot si nécessaire à l'aide de la commande suivante:
Je n'ai trouvé aucun problème en utilisant ceci sur la documentation.
la source
Si quelqu'un le recherche à l'avenir, cette méthode fonctionne pour moi avec OpenJDK 13.0.2.
J'ai de nombreuses classes dont j'ai besoin pour instancier dynamiquement au moment de l'exécution, chacune potentiellement avec un chemin de classe différent.
Dans ce code, j'ai déjà un objet appelé pack, qui contient des métadonnées sur la classe que j'essaie de charger. La méthode getObjectFile () renvoie l'emplacement du fichier de classe pour la classe. La méthode getObjectRootPath () renvoie le chemin d'accès au répertoire bin / contenant les fichiers de classe qui incluent la classe que j'essaie d'instancier. La méthode getLibPath () renvoie le chemin d'accès à un répertoire contenant les fichiers jar constituant le chemin d'accès aux classes du module dont la classe fait partie.
J'utilisais la dépendance Maven: org.xeustechnologies: jcl-core: 2.8 pour le faire auparavant, mais après avoir dépassé JDK 1.8, il se bloquait parfois et ne retournait jamais être coincé "en attente de références" sur Reference :: waitForReferencePendingList ().
Je garde également une carte des chargeurs de classe afin qu'ils puissent être réutilisés si la classe que j'essaie d'instancier est dans le même module qu'une classe que j'ai déjà instanciée, ce que je recommanderais.
la source
jetez un œil à ce projet que j'ai commencé: proxy-object lib
Cette bibliothèque chargera le pot à partir du système de fichiers ou de tout autre emplacement. Il dédiera un chargeur de classe pour le pot afin de s'assurer qu'il n'y a pas de conflits de bibliothèque. Les utilisateurs pourront créer n'importe quel objet à partir du pot chargé et appeler n'importe quelle méthode dessus. Cette bibliothèque a été conçue pour charger des fichiers jar compilés en Java 8 à partir de la base de code qui prend en charge Java 7.
Pour créer un objet:
ObjectBuilder prend en charge les méthodes d'usine, l'appel de fonctions statiques et les implémentations d'interfaces de rappel. je posterai plus d'exemples sur la page Lisezmoi.
la source
Cela peut être une réponse tardive, je peux le faire comme ceci (un exemple simple pour fastutil-8.2.2.jar) en utilisant la classe jhplot.Web de DataMelt ( http://jwork.org/dmelt )
Selon la documentation, ce fichier sera téléchargé dans "lib / user" puis chargé dynamiquement, vous pouvez donc commencer immédiatement à utiliser les classes de ce fichier jar dans le même programme.
la source
J'avais besoin de charger un fichier jar lors de l'exécution pour java 8 et java 9+ (les commentaires ci-dessus ne fonctionnent pas pour ces deux versions). Voici la méthode pour le faire (en utilisant Spring Boot 1.5.2 si cela peut se rapporter).
la source
Personnellement, je trouve que java.util.ServiceLoader fait assez bien le travail. Vous pouvez obtenir un exemple ici .
la source