Znajdź, skąd jest ładowana klasa java

184

Czy ktoś wie, jak programowo dowiedzieć się, skąd faktycznie ładuje klasę Java?

Często pracuję nad dużymi projektami, w których ścieżka klas jest bardzo długa, a ręczne wyszukiwanie nie jest tak naprawdę opcją. Ostatnio miałem problem z tym, że moduł ładujący klasę ładował niepoprawną wersję klasy, ponieważ znajdowała się ona na ścieżce klas w dwóch różnych miejscach.

Jak więc zmusić moduł ładujący klasę do powiedzenia mi, skąd na dysku pochodzi właściwy plik klasy?

Edycja: Co powiesz na to, że jeśli moduł ładujący klasę faktycznie nie załaduje klasy z powodu niedopasowania wersji (lub czegoś innego), czy w każdym razie moglibyśmy dowiedzieć się, jaki plik próbuje odczytać przed odczytaniem?

Łukasz
źródło
4
@JarrodRoberson Nie sądzę, to należy uznać za duplikat stackoverflow.com/questions/11747833/... ponieważ kwestia ta została poproszona w 2008 roku i które pytano w 2012 roku
Łk

Odpowiedzi:

189

Oto przykład:

package foo;

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

Wydrukowano:

file:/C:/Users/Jon/Test/foo/Test.class
Jon Skeet
źródło
32
Aby ograniczyć zbędne pisanie, można również użyć krótszej wersji:, Test.class.getResource("Test.class")która nie powtarza nazwy pakietu.
meriton
1
Co się stanie, jeśli klasa zostanie skompilowana, np. Z pliku .groovy?
Ondra Žižka
35
@meriton: Lub, aby przetrwać refactorinsgs:Test.class.getResource(Test.class.getSimpleName() + ".class")
leonbloy
1
Dla BouncyCastleProviderpełnej nazwy pakietu jest jednak wymagane.
Pavel Vlasov
3
Istnieje możliwość getClassLoader()zwrotu null. Zobacz tutaj rozszerzenie tej metody do obsługi tego.
OldCurmudgeon
100

Innym sposobem, aby dowiedzieć się, skąd ładowana jest klasa (bez manipulowania źródłem) jest uruchomienie wirtualnej maszyny Java z opcją: -verbose:class

jiriki
źródło
6
działało to bardzo dobrze i nie ma problemu z radzeniem sobie z klasami o zerowej wartości ClassLoader
lexicalscope
2
@ries Jeśli nie trzeba tego robić programowo, jest to zdecydowanie najlepsza droga i rozwiązało to mój problem. Jednak PO zapytał konkretnie, jak to zrobić programowo.
SantiBailors
80
getClass().getProtectionDomain().getCodeSource().getLocation();
Dave DiFranco
źródło
4
Tak, chociaż nie działa z zainstalowanym menedżerem bezpieczeństwa i bez wymaganych uprawnień.
Tom Hawtin - tackline
1
FYI, NPE = wyjątek zerowego wskaźnika. HTH!
Jewgienij Siergiejew
Ta metoda jest preferowana, o ile istnieje odwołanie do instancji, ponieważ można załadować tę samą klasę z dwóch różnych lokalizacji.
Miguel Ping
1
Nie działa również po wywołaniu z modułu Java 9+ (czego oczywiście nie mogłeś znać w 2008 roku).
Jeff G
28

Z tego korzystamy:

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

Będzie to działać w zależności od implementacji ClassLoader: getClass().getProtectionDomain().getCodeSource().getLocation()

Jevgeni Kabanov
źródło
17

Wersja Jona kończy się niepowodzeniem, gdy obiekt ClassLoaderjest zarejestrowany jako, nullco wydaje się sugerować, że został załadowany przez Boot ClassLoader.

Ta metoda rozwiązuje ten 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
źródło
5

Edytuj tylko pierwszą linię: Main.class

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

System.out.println(path);

Wynik:

/C:/Users/Test/bin/

Może zły styl, ale działa dobrze!

ecerer
źródło
3

Zazwyczaj nie robimy tego, czego używać na stałe. Możemy najpierw uzyskać className, a następnie użyć ClassLoader, aby uzyskać URL klasy.

        String className = MyClass.class.getName().replace(".", "/")+".class";
        URL classUrl  = MyClass.class.getClassLoader().getResource(className);
        String fullPath = classUrl==null ? null : classUrl.getPath();
Hongyang
źródło
Musi to być: URL classUrl = MyClass.class.getClassLoader (). GetResource ("/" + className);
Andrew Coates,
MyClass.class jest ważną częścią - getClass () może zwrócić Proxy! Następnie możesz uzyskać nazwę MyClass $$ EnhancerBySpringCGLIB $$ a98db882.class i zerowy adres URL.
jalmasi
1

Spójrz na to podobne pytanie. Narzędzie do odkrywania tej samej klasy ..

Myślę, że najistotniejszą przeszkodą jest posiadanie niestandardowego modułu ładującego klasy (ładowanie z db lub ldap)

OscarRyz
źródło
1

Prosta droga:

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

Nasz przykład:

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

Lub

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

Nasz przykład:

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

seduardo
źródło
0

To podejście działa zarówno w przypadku plików, jak i słoików:

Class clazz = Class.forName(nameOfClassYouWant);

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

Zakładając, że pracujesz z klasą o nazwie MyClass, powinny działać następujące czynności:

MyClass.class.getClassLoader();

To, czy można uzyskać lokalizację pliku .class na dysku, zależy od samego programu ładującego klasy. Na przykład, jeśli używasz czegoś takiego jak BCEL, pewna klasa może nawet nie mieć reprezentacji na dysku.

Daniel Spiewak
źródło
Zwraca ClassLoader użyty do załadowania klasy, prawda? Nie można znaleźć pliku .class?
Koray Tugay
1
Nie, nie ma. Program ładujący klasy może faktycznie odnosić się do zupełnie innej ścieżki klasy - oznacza to, że nie będzie w stanie całkowicie zmienić rzeczywistej lokalizacji klasy.
Tomáš Zato - Przywróć Monikę