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?


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

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


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();

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()


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";
}

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!


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();

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

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


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

-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.


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
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.