Temukan dari mana kelas java diambil

184

Apakah ada yang tahu bagaimana cara pemrograman menemukan di mana java classloader sebenarnya memuat kelas dari?

Saya sering mengerjakan proyek besar di mana classpath menjadi sangat panjang dan pencarian manual bukan pilihan. Saya baru-baru ini memiliki masalah di mana classloader memuat versi kelas yang tidak benar karena ada di classpath di dua tempat yang berbeda.

Jadi bagaimana saya bisa mendapatkan classloader untuk memberi tahu saya di mana pada disk file kelas yang sebenarnya berasal?

Sunting: Bagaimana jika classloader benar-benar gagal memuat kelas karena ketidakcocokan versi (atau sesuatu yang lain), adakah di sana kita bisa mengetahui file apa yang coba dibaca sebelum membacanya?

Lukas
sumber
4
@JarrodRoberson Saya tidak berpikir ini harus dianggap duplikat dari stackoverflow.com/questions/11747833/… karena pertanyaan ini ditanyakan pada tahun 2008 dan pertanyaan itu ditanyakan pada tahun 2012
Lukas

Jawaban:

189

Ini sebuah contoh:

package foo;

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

Ini dicetak:

file:/C:/Users/Jon/Test/foo/Test.class
Jon Skeet
sumber
32
Untuk mengurangi pengetikan yang berlebihan, kita juga bisa menggunakan versi yang lebih pendek:, Test.class.getResource("Test.class")yang tidak mengulangi nama paket.
meriton
1
Bagaimana jika kelas dikompilasi, misalnya dari file .groovy?
Ondra Žižka
35
@meriton: Atau, untuk bertahan hidup refactorinsgs:Test.class.getResource(Test.class.getSimpleName() + ".class")
leonbloy
1
Namun untuk BouncyCastleProvidernama paket lengkap diperlukan.
Pavel Vlasov
3
Dimungkinkan untuk getClassLoader()kembali null. Lihat di sini untuk ekstensi ke metode ini untuk mengatasinya.
OldCurmudgeon
100

Cara lain untuk mengetahui dari mana kelas diambil (tanpa memanipulasi sumber) adalah memulai Java VM dengan opsi: -verbose:class

jiriki
sumber
6
ini bekerja sangat baik, dan tidak memiliki masalah berurusan dengan kelas-kelas dengan null ClassLoader
lexicalscope
2
@ries Jika seseorang tidak perlu melakukan ini secara terprogram, ini jelas merupakan cara untuk pergi, dan itu memang memecahkan masalah saya. Namun, OP telah bertanya secara spesifik bagaimana melakukan ini secara terprogram.
SantiBailors
80
getClass().getProtectionDomain().getCodeSource().getLocation();
Dave DiFranco
sumber
4
Yup, meskipun itu tidak bekerja dengan manajer keamanan diinstal dan tanpa izin yang diperlukan.
Tom Hawtin - tackline
1
FYI, NPE = Null Pointer Exception. HTH!
Evgeni Sergeev
Metode ini lebih disukai selama Anda memiliki referensi ke sebuah instance, karena Anda dapat memuat kelas yang sama dari dua lokasi yang berbeda.
Miguel Ping
1
Juga tidak berfungsi ketika dipanggil dari modul Java 9+ (yang tentu saja Anda tidak tahu pada 2008).
Jeff G
28

Inilah yang kami gunakan:

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

Ini akan bekerja tergantung pada implementasi ClassLoader: getClass().getProtectionDomain().getCodeSource().getLocation()

Jevgeni Kabanov
sumber
17

Versi Jon gagal ketika objek ClassLoaderterdaftar sebagai nullyang tampaknya menyiratkan bahwa itu dimuat oleh Boot ClassLoader.

Metode ini berkaitan dengan masalah itu:

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
sumber
5

Sunting hanya baris pertama: Main.class

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

System.out.println(path);

Keluaran:

/C:/Users/Test/bin/

Mungkin gaya yang buruk tetapi bekerja dengan baik!

ecerer
sumber
3

Biasanya, kami tidak menggunakan hardcoding. Kita bisa mendapatkan className terlebih dahulu, dan kemudian menggunakan ClassLoader untuk mendapatkan URL kelas.

        String className = MyClass.class.getName().replace(".", "/")+".class";
        URL classUrl  = MyClass.class.getClassLoader().getResource(className);
        String fullPath = classUrl==null ? null : classUrl.getPath();
Hongyang
sumber
Perlu: URL classUrl = MyClass.class.getClassLoader (). GetResource ("/" + className);
Andrew Coates
MyClass.class adalah bagian penting - getClass () dapat mengembalikan Proxy! Maka Anda bisa mendapatkan nama seperti MyClass $$ EnhancerBySpringCGLIB $$ a98db882.class, dan null URL.
jalmasi
1

Lihatlah pertanyaan serupa ini. Alat untuk menemukan kelas yang sama ..

Saya pikir kendala yang paling relevan adalah jika Anda memiliki classloader khusus (memuat dari db atau ldap)

OscarRyz
sumber
1

Cara sederhana:

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

Contoh Keluar:

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

Atau

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

Contoh Keluar:

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

seduardo
sumber
0

Pendekatan ini berfungsi untuk file dan guci:

Class clazz = Class.forName(nameOfClassYouWant);

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

Dengan asumsi bahwa Anda bekerja dengan kelas bernama MyClass, berikut ini harus berfungsi:

MyClass.class.getClassLoader();

Apakah Anda bisa mendapatkan lokasi di-disk dari file .class tergantung pada classloader itu sendiri. Misalnya, jika Anda menggunakan sesuatu seperti BCEL, kelas tertentu bahkan mungkin tidak memiliki representasi di-disk.

Daniel Spiewak
sumber
Ini mengembalikan ClassLoader yang digunakan untuk memuat kelas, bukan? Tidak menemukan di mana file .class berada?
Koray Tugay
1
Tidak, tidak. Classloader sebenarnya dapat merujuk ke jalur kelas yang sama sekali berbeda - artinya itu akan sama sekali tidak dapat mengubah lokasi kelas yang sebenarnya.
Tomáš Zato - Pasang kembali Monica