Finden Sie heraus, woher die Java-Klasse geladen wird

183

Weiß jemand, wie man programmgesteuert herausfindet, woher der Java-Klassenladeprogramm die Klasse tatsächlich lädt?

Ich arbeite oft an großen Projekten, bei denen der Klassenpfad sehr lang wird und die manuelle Suche keine Option ist. Ich hatte kürzlich ein Problem, bei dem der Klassenladeprogramm eine falsche Version einer Klasse lud, weil sie sich an zwei verschiedenen Stellen im Klassenpfad befand.

Wie kann ich den Klassenladeprogramm dazu bringen, mir mitzuteilen, woher auf der Festplatte die eigentliche Klassendatei stammt?

Bearbeiten: Was ist, wenn der Klassenladeprogramm die Klasse aufgrund einer Versionsinkongruenz (oder etwas anderem) nicht laden kann? Können wir trotzdem herausfinden, welche Datei er zu lesen versucht, bevor er sie liest?

Luke
quelle
4
@JarrodRoberson Ich glaube nicht , das ein Duplikat in Betracht gezogen werden sollte stackoverflow.com/questions/11747833/... da diese Frage im Jahr 2008 und die Frage im Jahr 2012 gefragt wurde , gefragt wurde
luke

Antworten:

187

Hier ist ein Beispiel:

package foo;

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

Dies ausgedruckt:

file:/C:/Users/Jon/Test/foo/Test.class
Jon Skeet
quelle
32
Um redundante Eingaben zu vermeiden, können Sie auch die kürzere Version verwenden : Test.class.getResource("Test.class"), die den Paketnamen nicht wiederholt.
Meriton
1
Was ist, wenn die Klasse kompiliert wird, z. B. aus einer .groovy-Datei?
Ondra Žižka
35
@meriton: Oder, um Refactorinsgs zu überleben:Test.class.getResource(Test.class.getSimpleName() + ".class")
Leonbloy
1
Für den BouncyCastleProvidervollständigen Paketnamen ist jedoch erforderlich.
Pavel Vlasov
3
Es ist möglich getClassLoader(), zurückzukehren null. Sehen Sie hier für eine Erweiterung dieser Methode zu behandeln , die.
OldCurmudgeon
100

Eine andere Möglichkeit, herauszufinden, woher eine Klasse geladen wird (ohne die Quelle zu manipulieren), besteht darin, die Java-VM mit der folgenden Option zu starten: -verbose:class

Jiriki
quelle
6
Dies funktionierte sehr gut und hat nicht das Problem, mit Klassen mit null ClassLoader
lexicalscope
2
@ries Wenn man dies nicht programmgesteuert tun muss, ist dies definitiv der richtige Weg, und es hat mein Problem gelöst. Das OP hatte jedoch speziell gefragt, wie dies programmatisch erfolgen soll.
SantiBailors
79
getClass().getProtectionDomain().getCodeSource().getLocation();
Dave DiFranco
quelle
4
Ja, obwohl es nicht mit einem installierten Sicherheitsmanager und ohne die erforderlichen Berechtigungen funktioniert.
Tom Hawtin - Tackline
1
Zu Ihrer Information, NPE = Nullzeiger-Ausnahme. HTH!
Evgeni Sergeev
Diese Methode wird bevorzugt, solange Sie einen Verweis auf eine Instanz haben, da Sie dieselbe Klasse von zwei verschiedenen Speicherorten laden können.
Miguel Ping
1
Funktioniert auch nicht, wenn es von einem Java 9+ Modul aufgerufen wird (was Sie 2008 natürlich nicht gewusst haben könnten).
Jeff G
28

Dies ist, was wir verwenden:

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

Dies funktioniert abhängig von der ClassLoader-Implementierung: getClass().getProtectionDomain().getCodeSource().getLocation()

Jevgeni Kabanov
quelle
17

Jons Version schlägt fehl, wenn das Objekt ClassLoaderregistriert ist, nullwas darauf hindeutet, dass es vom Boot geladen wurde ClassLoader.

Diese Methode behandelt dieses Problem:

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";
}
OldCurmudgeon
quelle
5

Bearbeiten Sie nur die erste Zeile: Main.class

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

System.out.println(path);

Ausgabe:

/C:/Users/Test/bin/

Vielleicht schlechter Stil, funktioniert aber gut!

ecerer
quelle
3

Normalerweise verwenden wir keine Hardcodierung. Wir können zuerst className abrufen und dann ClassLoader verwenden, um die Klassen-URL abzurufen.

        String className = MyClass.class.getName().replace(".", "/")+".class";
        URL classUrl  = MyClass.class.getClassLoader().getResource(className);
        String fullPath = classUrl==null ? null : classUrl.getPath();
Hongyang
quelle
Muss sein: URL classUrl = MyClass.class.getClassLoader (). GetResource ("/" + className);
Andrew Coates
MyClass.class ist ein wichtiger Teil - getClass () kann Proxy zurückgeben! Dann können Sie einen Namen wie MyClass $$ EnhancerBySpringCGLIB $$ a98db882.class und eine Null-URL erhalten.
Jalmasi
1

Schauen Sie sich diese ähnliche Frage an. Werkzeug, um die gleiche Klasse zu entdecken ..

Ich denke, das wichtigste Hindernis ist, wenn Sie einen benutzerdefinierten Klassenladeprogramm haben (Laden von einer Datenbank oder einer LDAP).

OscarRyz
quelle
1

Einfacher Weg:

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

Unser Beispiel:

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

Oder

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

Unser Beispiel:

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

Verführer
quelle
0

Dieser Ansatz funktioniert sowohl für Dateien als auch für Gläser:

Class clazz = Class.forName(nameOfClassYouWant);

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

Angenommen, Sie arbeiten mit einer Klasse namens MyClass, sollte Folgendes funktionieren:

MyClass.class.getClassLoader();

Ob Sie den Speicherort der .class-Datei auf der Festplatte abrufen können oder nicht, hängt vom Klassenladeprogramm selbst ab. Wenn Sie beispielsweise BCEL verwenden, verfügt eine bestimmte Klasse möglicherweise nicht einmal über eine Darstellung auf der Festplatte.

Daniel Spiewak
quelle
Dies gibt den ClassLoader zurück, der zum Laden der Klasse verwendet wird, nicht wahr? Es wird nicht gefunden, wo sich die .class-Datei befindet?
Koray Tugay
1
Nein, tut es nicht. Der Klassenladeprogramm kann tatsächlich auf einen völlig anderen Klassenpfad verweisen - was bedeutet, dass er den tatsächlichen Klassenort überhaupt nicht mehr ermitteln kann.
Tomáš Zato - Wiedereinsetzung Monica