web-dev-qa-db-fra.com

Comment obtenir classpath de classloader?

J'utilise un code tiers qui, lorsqu'un argument de ligne de commande '-classpath' est défini, ne définit pas Java.class.path, mais crée simplement un chargeur de classe. Il ajoute toutes les URL des éléments de la ligne de commande. , puis le définit comme chargeur de classe de contexte . Dans une classe de plugin correspondant à ce code que j’ai écrit, j’obtiens une instance de ce chargeur de classe, et j’ai besoin de l’utiliser pour récupérer le chemin de classe sous-jacent. peut l’utiliser dans un appel de JavaCompiler.getTask (...) et compiler un autre code à la volée . Cependant, il ne semble pas qu’il soit possible d’obtenir le ClassPath à partir du ClassLoader, et en tant que Java.class. path is unset, je n'arrive pas à accéder au classpath sous-jacent avec lequel l'application a été invoquée à l'origine ... Des idées?

45
Marcus Mathioudakis

Si le chargeur de classe utilise des URL, il doit s'agir d'une URLClassloader. Vous avez accès aux URL qui définissent le classpath ainsi que son parent ClassLoader.

Pour obtenir les URL, procédez comme suit:

((URLClassLoader) (Thread.currentThread().getContextClassLoader())).getURLs()
54
Adel Boutros

MISE À JOUR: Ma réponse originale ci-dessous est terriblement inadéquate, maintenant que j'ai passé trois ans à développer FastClasspathScanner et qu'un grand nombre de rapports de bogues ont été enregistrés concernant certains environnements de classpath ne fonctionnant pas avec cette bibliothèque. FastClasspathScanner gère maintenant de nombreux mécanismes complexes de spécification de classpath . Le simple fait de trouver le chemin de classe peut s'avérer extrêmement compliqué dans le cas général (encore moins de le scanner), car il existe de nombreuses façons d'ajouter des fichiers JAR et des répertoires au chemin de classe.

D'une part, le code que j'ai donné ci-dessous ne gère que URLClassLoader, et de nombreux principaux environnements et conteneurs d'exécution ne l'étendent pas, ils implémentent leur propre chargeur de classes à partir de zéro. Mais cela devient beaucoup plus compliqué que cela dans le cas de Java 9+, car même si le classpath traditionnel existe toujours, tout ira dans le futur en utilisant le chemin du module, pas le classpath. Les modules ont des URL, mais ce sont des URL "jrt:/", pas des URL "file:/", et les URL de module n'incluent pas réellement un chemin de fichier, mais uniquement le nom du module - vous ne pouvez donc même pas localiser le module sur le disque. Votre seule option consiste à utiliser le système de modules (fortement encapsulé) pour travailler avec des modules. J'ai écrit sur la façon de scanner le chemin du module ici .

FastClasspathScanner gère de nombreux mécanismes complexes de spécification de chemins de classes, vous n'avez donc pas besoin de réinventer la roue. Vous pouvez récupérer une liste des entrées de chemin de classe à partir de FastClasspathScanner - cela vous évitera d'avoir à essayer de faire fonctionner quelque chose avec tous les divers mécanismes de spécification de chemin de classe que vous trouverez dans la nature. (Excuses si ce dernier lien est rompu - l’API et la documentation pour FCS changeront bientôt.)

-

[Ancienne réponse - obsolète:]

Les autres réponses sont correctes dans la plupart des situations, mais cela devient plus compliqué que dans certains contextes: par exemple. Maven , Tomcat et JUnit ont leur propre support de classpath et n'utilisent pas le classpath du système.

Jusqu'à présent, c'est le système le plus complet que j'ai réussi à créer (ce code provient de mon Fast Classpath Scanner project):

/** The unique elements of the classpath, as an ordered list. */
private final ArrayList<File> classpathElements = new ArrayList<>();

/** The unique elements of the classpath, as a set. */
private final HashSet<String> classpathElementsSet = new HashSet<>();

/** Clear the classpath. */
private void clearClasspath() {
    classpathElements.clear();
    classpathElementsSet.clear();
}

/** Add a classpath element. */
private void addClasspathElement(String pathElement) {
    if (classpathElementsSet.add(pathElement)) {
        final File file = new File(pathElement);
        if (file.exists()) {
            classpathElements.add(file);
        }
    }
}

/** Parse the system classpath. */
private void parseSystemClasspath() {
    // Look for all unique classloaders.
    // Keep them in an order that (hopefully) reflects the order in which class resolution occurs.
    ArrayList<ClassLoader> classLoaders = new ArrayList<>();
    HashSet<ClassLoader> classLoadersSet = new HashSet<>();
    classLoadersSet.add(ClassLoader.getSystemClassLoader());
    classLoaders.add(ClassLoader.getSystemClassLoader());
    if (classLoadersSet.add(Thread.currentThread().getContextClassLoader())) {
        classLoaders.add(Thread.currentThread().getContextClassLoader());
    }
    // Dirty method for looking for any other classloaders on the call stack
    try {
        // Generate stacktrace
        throw new Exception();
    } catch (Exception e) {
        StackTraceElement[] stacktrace = e.getStackTrace();
        for (StackTraceElement elt : stacktrace) {
            try {
                ClassLoader cl = Class.forName(elt.getClassName()).getClassLoader();
                if (classLoadersSet.add(cl)) {
                    classLoaders.add(cl);
                }
            } catch (ClassNotFoundException e1) {
            }
        }
    }

    // Get file paths for URLs of each classloader.
    clearClasspath();
    for (ClassLoader cl : classLoaders) {
        if (cl != null) {
            for (URL url : ((URLClassLoader) cl).getURLs()) {
                if ("file".equals(url.getProtocol())) {
                    addClasspathElement(url.getFile());
                }
            }
        }
    }
}

/** Override the system classpath with a custom classpath to search. */
public FastClasspathScanner overrideClasspath(String classpath) {
    clearClasspath();
    for (String pathElement : classpath.split(File.pathSeparator)) {
        addClasspathElement(pathElement);
    }
    return this;
}

/**
 * Get a list of unique elements on the classpath (directories and files) as File objects, preserving order.
 * Classpath elements that do not exist are not included in the list.
 */
public ArrayList<File> getUniqueClasspathElements() {
    return classpathElements;
}
15
Luke Hutchison

Pour référence ultérieure, si vous devez passer le chemin de classe à ProcessBuilder:

StringBuffer buffer = new StringBuffer();
for (URL url :
    ((URLClassLoader) (Thread.currentThread()
        .getContextClassLoader())).getURLs()) {
  buffer.append(new File(url.getPath()));
  buffer.append(System.getProperty("path.separator"));
}
String classpath = buffer.toString();
int toIndex = classpath
    .lastIndexOf(System.getProperty("path.separator"));
classpath = classpath.substring(0, toIndex);
ProcessBuilder builder = new ProcessBuilder("Java",
    "-classpath", classpath, "com.a.b.c.TestProgram");
7
BJ Peter DeLaCruz

Si les autres réponses ne fonctionnent pas, essayez ceci:

ClassLoader cl = ClassLoader.getSystemClassLoader();
URL[] urls = ((URLClassLoader) cl).getURLs();
for (URL url: urls) {
    System.out.println(url.getFile());
}
5
Yavin5

Déposez ce code dans une page jsp vide pour afficher la hiérarchie de classLoader et les fichiers JAR associés chargés à chaque niveau.

la méthode visit () ci-dessous peut également être utilisée seule 

<%!
    public void visit(StringBuilder sb, int indent, ClassLoader classLoader) {
        if (indent > 20 || classLoader == null)
            return;
        String indentStr = new String(new char[indent]).replace("\0", "    ");
        sb.append("\n");
        sb.append(indentStr);
        sb.append(classLoader.getClass().getName());
        sb.append(":");
        if (classLoader instanceof Java.net.URLClassLoader) {
            Java.net.URL[] urls = ((Java.net.URLClassLoader)classLoader).getURLs();
            for (Java.net.URL url : urls) {
                sb.append("\n");
                sb.append(indentStr);
                sb.append(url);
            }
        }
        sb.append("\n");
        visit(sb, indent + 1, classLoader.getParent());
    }

%>

<%
StringBuilder sb = new StringBuilder();
visit(sb,1,this.getClass().getClassLoader());
%>
<pre>
<%=sb%>
</pre>
0
Prashant Bhate