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?


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

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


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

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


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

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!


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

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

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


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

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


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ę
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.