web-dev-qa-db-fra.com

Comment changez-vous le CLASSPATH dans Java?

Comment changez-vous le CLASSPATH d'un processus Java à partir du processus Java?


Avant de me demander "Pourquoi voudriez-vous faire ça?" Je vais l'expliquer sous peu.

Lorsque vous avez un Clojure REPL en cours d'exécution, il est courant d'avoir besoin de plus de jars dans votre CLASSPATH pour charger un fichier source Clojure , et j'aimerais le faire sans avoir pour redémarrer Clojure lui-même (ce qui n'est pas vraiment une option lors de son utilisation sur Slime sur Emacs).

C'est la raison, mais je ne veux pas que cette question soit étiquetée comme un éditeur un peu bizarre et ne soit pas prise en compte par la majorité des développeurs Java Java qui peuvent avoir la réponse.

59
pupeno

Mise à jour Q4 2017: comme commenté ci-dessous par vda8888 , dans Java 9, le système Java.lang.ClassLoader n'est plus un Java.net.URLClassLoader .

Voir " Guide de migration Java 9: ​​les sept défis les plus courants "

La stratégie de chargement de classe que je viens de décrire est implémentée dans un nouveau type et dans Java 9 le chargeur de classe d'application est de ce type.
Cela signifie que ce n'est plus un URLClassLoader, donc les séquences occasionnelles (URLClassLoader) getClass().getClassLoader() Ou (URLClassLoader) ClassLoader.getSystemClassLoader() Ne seront plus exécutées.

Java.lang.ModuleLayer serait une approche alternative utilisée pour influencer le modulepath (au lieu du chemin de classe). Voir par exemple " modules Java 9 - Bases JPMS ".


Pour Java 8 ou inférieur:

Quelques remarques générales:

vous ne pouvez pas (d'une manière portable qui est garantie de fonctionner, voir ci-dessous) changer le chemin de classe du système. Au lieu de cela, vous devez définir un nouveau ClassLoader.

Les ClassLoaders fonctionnent de manière hiérarchique ... donc toute classe qui fait une référence statique à la classe X doit être chargée dans le même ClassLoader que X, ou dans un ClassLoader enfant. Vous ne pouvez PAS utiliser un ClassLoader personnalisé pour faire charger correctement le code par le lien ClassLoader du système, s'il ne l'avait pas fait auparavant. Vous devez donc organiser l'exécution de votre code d'application principal dans le ClassLoader personnalisé en plus du code supplémentaire que vous recherchez.
(Cela étant dit, cracked-all mentionne dans les commentaires cet exemple de extension du URLClassLoader )

Et vous pourriez envisager de ne pas écrire votre propre ClassLoader, mais utilisez simplement URLClassLoader à la place. Créez un URLClassLoader avec une URL qui ne soit pas dans l'URL des chargeurs de classe parents.

URL[] url={new URL("file://foo")};
URLClassLoader loader = new URLClassLoader(url);

Une solution plus complète serait:

ClassLoader currentThreadClassLoader
 = Thread.currentThread().getContextClassLoader();

// Add the conf dir to the classpath
// Chain the current thread classloader
URLClassLoader urlClassLoader
 = new URLClassLoader(new URL[]{new File("mtFile").toURL()},
                      currentThreadClassLoader);

// Replace the thread classloader - assumes
// you have permissions to do so
Thread.currentThread().setContextClassLoader(urlClassLoader);

Si vous supposez que le chargeur de classe système des JVM est un URLClassLoader (ce qui peut ne pas être vrai pour toutes les JVM), vous pouvez également utiliser la réflexion pour modifier réellement le chemin de classe du système ... (mais c'est un hack;)):

public void addURL(URL url) throws Exception {
  URLClassLoader classLoader
         = (URLClassLoader) ClassLoader.getSystemClassLoader();
  Class clazz= URLClassLoader.class;

  // Use reflection
  Method method= clazz.getDeclaredMethod("addURL", new Class[] { URL.class });
  method.setAccessible(true);
  method.invoke(classLoader, new Object[] { url });
}

addURL(new File("conf").toURL());

// This should work now!
Thread.currentThread().getContextClassLoader().getResourceAsStream("context.xml");
78
VonC

Je ne crois pas que vous puissiez - la bonne chose à faire (je crois) est de créer un nouveau chargeur de classe avec le nouveau chemin. Alternativement, vous pouvez écrire votre propre chargeur de classe qui vous permet de changer dynamiquement le chemin de classe (pour ce chargeur).

3
Jon Skeet
2
myfreeweb

Vous voudrez peut-être étudier l'utilisation de Java.net.URLClassLoader . Il vous permet de charger par programme des classes qui n'étaient pas à l'origine dans votre chemin de classe, bien que je ne sois pas sûr que ce soit exactement ce dont vous avez besoin.

1
Jack Leow

Il est possible, comme le montrent les deux liens ci-dessous, la méthode donnée par VonC semble être la meilleure, mais consultez certains de ces messages et recherchez Google pour "Java Dynamic Classpath" ou "Java Dynamic Class Loading" et découvrez quelques informations à partir de là.

Je publierais plus en détail, mais VonC a pratiquement fait le travail.

De Chargement dynamique des fichiers de classe et Jar .

Vérifiez également ceci Message du forum Sun .

1
Henry B