Trouver où la classe java est chargée

184

Est-ce que quelqu'un sait comment découvrir par programmation d'où le chargeur de classe Java charge réellement la classe?

Je travaille souvent sur de gros projets où le chemin des classes devient très long et la recherche manuelle n'est pas vraiment une option. J'ai récemment eu un problème où le classloader chargeait une version incorrecte d'une classe car il était sur le chemin de classe à deux endroits différents.

Alors, comment puis-je faire en sorte que le chargeur de classe me dise d'où vient le fichier de classe réel sur le disque?

Edit: Qu'en est-il si le chargeur de classe ne parvient pas à charger la classe en raison d'une incompatibilité de version (ou autre chose), est-il possible de savoir quel fichier il essaie de lire avant de le lire?

Luke
la source
4
@JarrodRoberson Je ne pense pas que cela devrait être considéré comme un double de stackoverflow.com/questions/11747833/… puisque cette question a été posée en 2008 et cette question a été posée en 2012
lu

Réponses:

189

Voici un exemple:

package foo;

public class Test
{
    public static void main(String[] args)
    {
        ClassLoader loader = Test.class.getClassLoader();
        System.out.println(loader.getResource("foo/Test.class"));
    }
}

Ceci a imprimé:

file:/C:/Users/Jon/Test/foo/Test.class
Jon Skeet
la source
32
Pour réduire la saisie redondante, on peut également utiliser la version plus courte:, Test.class.getResource("Test.class")qui ne répète pas le nom du paquet.
meriton
1
Que faire si la classe est compilée, par exemple à partir d'un fichier .groovy?
Ondra Žižka
35
@meriton: Ou, pour survivre à refactorinsgs:Test.class.getResource(Test.class.getSimpleName() + ".class")
leonbloy
1
Pour le BouncyCastleProvidernom complet du paquet est nécessaire cependant.
Pavel Vlasov
3
Il est possible getClassLoader()de revenir null. Voir ici pour une extension de cette méthode pour gérer cela.
OldCurmudgeon
100

Une autre façon de savoir à partir de laquelle une classe est chargée (sans manipuler la source) consiste à démarrer la machine virtuelle Java avec l'option: -verbose:class

jiriki
la source
6
cela a fonctionné très bien, et n'a pas le problème de traiter les classes avec null ClassLoader
lexicalscope
2
@ries Si on n'a pas besoin de faire cela par programme, c'est certainement la voie à suivre, et cela a résolu mon problème. Cependant, le PO avait demandé spécifiquement comment procéder par programme.
SantiBailors
80
getClass().getProtectionDomain().getCodeSource().getLocation();
Dave DiFranco
la source
4
Oui, bien que cela ne fonctionne pas avec un gestionnaire de sécurité installé et sans les autorisations requises.
Tom Hawtin - tackline
1
FYI, NPE = exception de pointeur nul. HTH!
Evgeni Sergeev
Cette méthode est préférable tant que vous disposez d'une référence à une instance, car vous pouvez charger la même classe à partir de deux emplacements différents.
Miguel Ping
1
Ne fonctionne pas non plus lorsqu'il est appelé depuis un module Java 9+ (ce que vous n'auriez bien sûr pas pu connaître en 2008).
Jeff G
28

Voici ce que nous utilisons:

public static String getClassResource(Class<?> klass) {
  return klass.getClassLoader().getResource(
     klass.getName().replace('.', '/') + ".class").toString();
}

Cela fonctionnera en fonction de l'implémentation de ClassLoader: getClass().getProtectionDomain().getCodeSource().getLocation()

Jevgeni Kabanov
la source
17

La version de Jon échoue lorsque l'objet ClassLoaderest enregistré comme nullce qui semble impliquer qu'il a été chargé par Boot ClassLoader.

Cette méthode traite ce problème:

public static String whereFrom(Object o) {
  if ( o == null ) {
    return null;
  }
  Class<?> c = o.getClass();
  ClassLoader loader = c.getClassLoader();
  if ( loader == null ) {
    // Try the bootstrap classloader - obtained from the ultimate parent of the System Class Loader.
    loader = ClassLoader.getSystemClassLoader();
    while ( loader != null && loader.getParent() != null ) {
      loader = loader.getParent();
    }
  }
  if (loader != null) {
    String name = c.getCanonicalName();
    URL resource = loader.getResource(name.replace(".", "/") + ".class");
    if ( resource != null ) {
      return resource.toString();
    }
  }
  return "Unknown";
}
VieuxCurmudgeon
la source
5

Modifier seulement la 1ère ligne: Main.class

Class<?> c = Main.class;
String path = c.getResource(c.getSimpleName() + ".class").getPath().replace(c.getSimpleName() + ".class", "");

System.out.println(path);

Production:

/C:/Users/Test/bin/

Peut-être mauvais style mais fonctionne bien!

ecerer
la source
3

En règle générale, nous ne savons pas quoi utiliser le codage en dur. Nous pouvons d'abord obtenir className, puis utiliser ClassLoader pour obtenir l'URL de la classe.

        String className = MyClass.class.getName().replace(".", "/")+".class";
        URL classUrl  = MyClass.class.getClassLoader().getResource(className);
        String fullPath = classUrl==null ? null : classUrl.getPath();
Hongyang
la source
Doit être: URL classUrl = MyClass.class.getClassLoader (). GetResource ("/" + className);
Andrew Coates le
MyClass.class est une partie importante - getClass () peut retourner Proxy! Ensuite, vous pouvez obtenir un nom comme MyClass $$ EnhancerBySpringCGLIB $$ a98db882.class et une URL nulle.
jalmasi le
1

Jetez un œil à cette question similaire. Outil pour découvrir la même classe.

Je pense que l'obstacle le plus pertinent est si vous avez un classloader personnalisé (chargement depuis une base de données ou un ldap)

OscarRyz
la source
1

Manière simple:

System.out.println (java.lang.String.class.getResource (String.class.getSimpleName () + ". Class"));

Exemple de sortie:

jar: fichier: / D: /Java/jdk1.8/jre/lib/rt.jar! /java/lang/String.class

Ou

String obj = "test simple"; System.out.println (obj.getClass (). GetResource (obj.getClass (). GetSimpleName () + ". Class"));

Exemple de sortie:

jar: fichier: / D: /Java/jdk1.8/jre/lib/rt.jar! /java/lang/String.class

Seduardo
la source
0

Cette approche fonctionne pour les fichiers et les fichiers JAR:

Class clazz = Class.forName(nameOfClassYouWant);

URL resourceUrl = clazz.getResource("/" + clazz.getCanonicalName().replace(".", "/") + ".class");
InputStream classStream = resourceUrl.openStream(); // load the bytecode, if you wish
Adam
la source
-1

En supposant que vous travaillez avec une classe nommée MyClass, ce qui suit devrait fonctionner:

MyClass.class.getClassLoader();

Le fait que vous puissiez ou non obtenir l'emplacement sur le disque du fichier .class dépend du chargeur de classe lui-même. Par exemple, si vous utilisez quelque chose comme BCEL, une certaine classe peut même ne pas avoir de représentation sur disque.

Daniel Spiewak
la source
Cela renvoie le ClassLoader utilisé pour charger la classe, n'est-ce pas? Il ne trouve pas où se trouve le fichier .class?
Koray Tugay du
1
Non, ce n'est pas le cas. Le chargeur de classe peut en fait se référer à un chemin de classe complètement différent - cela signifie qu'il sera totalement incapable de retrouver l'emplacement réel de la classe.
Tomáš Zato - Réintégrer Monica