Apakah mungkin untuk menemukan semua kelas atau antarmuka dalam paket yang diberikan? (Dengan cepat melihat misalnya Package
, sepertinya tidak.)
java
reflection
packages
Jonik
sumber
sumber
Jawaban:
Karena sifat dinamis loader kelas, ini tidak mungkin. Loader kelas tidak diharuskan memberi tahu VM kelas mana yang dapat diberikannya, sebaliknya mereka hanya menyerahkan permintaan untuk kelas, dan harus mengembalikan kelas atau melemparkan pengecualian.
Namun, jika Anda menulis loader kelas Anda sendiri, atau memeriksa classpath dan itu guci, itu mungkin untuk menemukan informasi ini. Ini akan melalui operasi filesystem, dan bukan refleksi. Bahkan mungkin ada perpustakaan yang dapat membantu Anda melakukan ini.
Jika ada kelas yang dihasilkan, atau dikirimkan dari jarak jauh, Anda tidak akan dapat menemukan kelas-kelas itu.
Metode yang normal adalah sebaliknya mendaftarkan suatu kelas yang perlu Anda akses di suatu file, atau mereferensikannya di kelas yang berbeda. Atau cukup gunakan konvensi ketika datang ke penamaan.
Tambahan: Perpustakaan Refleksi akan memungkinkan Anda untuk mencari kelas di classpath saat ini. Ini dapat digunakan untuk mendapatkan semua kelas dalam satu paket:
sumber
Anda mungkin harus melihat pada perpustakaan Refleksi open source . Dengan itu Anda dapat dengan mudah mencapai apa yang Anda inginkan.
Pertama, atur indeks refleksi (agak berantakan karena mencari semua kelas dinonaktifkan secara default):
Kemudian Anda dapat meminta semua objek dalam paket yang diberikan:
sumber
.addUrls(ClasspathHelper.forJavaClassPath())
alih-alih memecahkannya untuk saya. Lebih sedikit kode juga!Google Guava 14 menyertakan kelas baru
ClassPath
dengan tiga metode untuk memindai kelas tingkat atas:getTopLevelClasses()
getTopLevelClasses(String packageName)
getTopLevelClassesRecursive(String packageName)
Lihat
ClassPath
javadocs untuk info lebih lanjut.sumber
ClassPath
ditandai@Beta
, jadi mungkin bukan ide yang baik untuk beberapa ...Anda bisa menggunakan metode 1 ini yang menggunakan
ClassLoader
.__________
1 Metode ini diambil dari http://snippets.dzone.com/posts/show/4831 , yang diarsipkan oleh Internet Archive, seperti yang ditautkan sekarang. Cuplikan ini juga tersedia di https://dzone.com/articles/get-all-classes-within-package .
sumber
%20
, tetapinew File()
konstruktor menganggapnya sebagai persen literal yang menandakan dua nol. Saya memperbaikinya dengan mengubahdirs.add(...)
baris ini:dirs.add(new File(resource.toURI()));
Ini juga berarti saya harus menambahkanURISyntaxException
klausagetClasses
Musim semi
Contoh ini untuk Spring 4, tetapi Anda dapat menemukan pemindai classpath di versi sebelumnya juga.
Google Jambu
Catatan: Di versi 14, API masih ditandai sebagai @Beta , jadi berhati-hatilah dalam kode produksi.
sumber
ClassPath
kelas di Guava juga ditandai dengan@Beta
: "API yang ditandai dengan penjelasan @Beta di tingkat kelas atau metode dapat berubah. Mereka dapat dimodifikasi dengan cara apa pun, atau bahkan dihapus, dalam rilis besar apa pun. Jika kode Anda adalah perpustakaan itu sendiri (yaitu digunakan pada CLASSPATH pengguna di luar kendali Anda sendiri), Anda tidak boleh menggunakan API beta, kecuali jika Anda mengemasnya kembali ... " code.google.com/p/guava-libraries/#Important_WarningsgetAllClasses()
metode dapat digunakan.Halo. Saya selalu memiliki beberapa masalah dengan solusi di atas (dan di situs lain).
Saya, sebagai pengembang, sedang memprogram addon untuk API. API mencegah penggunaan perpustakaan eksternal atau alat pihak ketiga mana pun. Setup juga terdiri dari campuran kode dalam file jar atau zip dan file kelas yang terletak langsung di beberapa direktori. Jadi kode saya harus dapat bekerja diseluruh pengaturan. Setelah banyak penelitian saya menemukan metode yang akan bekerja di setidaknya 95% dari semua pengaturan yang mungkin.
Kode berikut pada dasarnya adalah metode kerja keras yang akan selalu berhasil.
Kode:
Kode ini memindai paket yang diberikan untuk semua kelas yang termasuk di dalamnya. Ini hanya akan berfungsi untuk semua kelas saat ini
ClassLoader
.Tiga metode ini memberi Anda kemampuan untuk menemukan semua kelas dalam paket yang diberikan.
Anda menggunakannya seperti ini:
Penjelasan:
Metode pertama mendapatkan arus
ClassLoader
. Itu kemudian mengambil semua sumber daya yang berisi paket kata dan iterates dari iniURL
. Itu kemudian menciptakanURLConnection
dan menentukan apa jenis URl yang kita miliki. Ini bisa berupa direktori (FileURLConnection
) atau direktori di dalam jar atau file zip (JarURLConnection
). Bergantung pada jenis koneksi apa kita memiliki dua metode berbeda akan dipanggil.Pertama mari kita lihat apa yang terjadi jika itu a
FileURLConnection
.Pertama-tama memeriksa apakah File yang dilewati ada dan merupakan direktori. Jika itu masalahnya memeriksa apakah itu adalah file kelas. Jika demikian suatu
Class
objek akan dibuat dan dimasukkan ke dalamArrayList
. Jika ini bukan file kelas tetapi merupakan direktori, kita cukup beralih ke dalamnya dan melakukan hal yang sama. Semua case / file lainnya akan diabaikan.Jika
URLConnection
ini adalahJarURLConnection
metode pembantu pribadi lainnya akan dipanggil. Metode ini mengulangi semua Entri dalam arsip zip / jar. Jika satu entri adalah file kelas dan ada di dalam paketClass
objek akan dibuat dan disimpan diArrayList
.Setelah semua sumber daya telah diuraikan itu (metode utama) mengembalikan isi
ArrayList
semua kelas dalam paket yang diberikan, yang saat iniClassLoader
tahu tentang.Jika proses gagal pada suatu titik a
ClassNotFoundException
akan dilemparkan containg informasi rinci tentang penyebab pastinya.sumber
sun.net.www.protocol.file.FileURLConnection
, yang menghasilkan peringatan pada waktu kompilasi ("peringatan: sun.net.www.protocol.file.FileURLConnection adalah Sun proprietary API dan dapat dihapus dalam rilis mendatang"). Apakah ada alternatif untuk menggunakan kelas itu, atau bisakah peringatan itu ditekan menggunakan anotasi?if ( ... instanceof JarURLConnecton) { ... } else { // Asume that the Connection is valid and points to a File }
Ini yang saya lakukan pada kode saya untuk mencari kelas beranotasi JPATanpa menggunakan perpustakaan tambahan:
sumber
upackage
adalahnull
... :(String pack = getPackage().getName();
, maka Anda harus menambahkanpack = pack.replaceAll("[.]", "/");
Secara umum pemuat kelas tidak memungkinkan untuk memindai semua kelas pada classpath. Tetapi biasanya satu-satunya kelas loader yang digunakan adalah UrlClassLoader dari mana kita dapat mengambil daftar direktori dan file jar (lihat getURLs ) dan buka satu per satu untuk mendaftar kelas yang tersedia. Pendekatan ini, yang disebut pemindaian jalur kelas, diimplementasikan dalam Pemindaian dan Refleksi .
Pendekatan lain adalah dengan menggunakan Java Pluggable Annotation Processing API untuk menulis prosesor anotasi yang akan mengumpulkan semua kelas beranotasi pada waktu kompilasi dan membangun file indeks untuk penggunaan runtime. Mekanisme ini diterapkan di perpustakaan ClassIndex :
Perhatikan bahwa tidak ada pengaturan tambahan yang diperlukan karena pemindaian sepenuhnya otomatis berkat Java compiler yang secara otomatis menemukan prosesor yang ditemukan di classpath.
sumber
Mekanisme yang paling kuat untuk mendaftar semua kelas dalam paket yang diberikan saat ini adalah ClassGraph , karena menangani sederet mungkin mekanisme spesifikasi classpath , termasuk sistem modul JPMS yang baru. (Akulah penulisnya.)
sumber
Begini cara saya melakukannya. Saya memindai semua subfolder (sub paket) dan saya tidak mencoba memuat kelas anonim:
sumber
Saya mengumpulkan proyek github sederhana yang memecahkan masalah ini:
https://github.com/ddopson/java-class-enumerator
Ini seharusnya berfungsi untuk KEDUA classpath berbasis file DAN untuk file jar.
Jika Anda menjalankan 'make' setelah memeriksa proyek itu akan mencetak ini:
Lihat juga jawaban saya yang lain
sumber
Ya menggunakan beberapa API Anda bisa, di sini adalah bagaimana saya suka melakukannya, menghadapi masalah ini yang saya gunakan hibernate core & harus menemukan kelas yang mana dijelaskan dengan penjelasan tertentu.
Jadikan ini anotasi khusus dengan menggunakan mana Anda akan menandai kelas mana yang ingin Anda ambil.
Kemudian tandai kelas Anda dengan itu seperti
Buat kelas utilitas ini yang memiliki metode berikut
Panggil metode allFoundClassesAnnotatedWithEntityToBeScanned () untuk mendapatkan Set of Classes yang ditemukan.
Anda akan membutuhkan lib yang diberikan di bawah ini
sumber
Anda perlu mencari setiap entri pemuat kelas di jalur kelas:
Jika entri adalah direktori, cari saja di subdirektori yang tepat:
Jika entri adalah file, dan itu jar, periksa entri ZIP itu:
Sekarang setelah Anda memiliki semua paket nama yang ada dalam paket, Anda dapat mencoba memuatnya dengan refleksi dan menganalisis apakah mereka adalah kelas atau antarmuka, dll.
sumber
Saya sudah mencoba menggunakan perpustakaan Reflections, tetapi ada beberapa masalah dalam menggunakannya, dan ada terlalu banyak toples yang harus saya masukkan hanya untuk mendapatkan kelas-kelas pada sebuah paket.
Saya akan memposting solusi yang saya temukan dalam pertanyaan duplikat ini: Bagaimana cara mendapatkan semua nama kelas dalam sebuah paket?
The jawaban ditulis oleh sp00m ; Saya telah menambahkan beberapa koreksi untuk membuatnya berfungsi:
Untuk menggunakannya cukup panggil metode find seperti sp00n yang disebutkan dalam contoh ini: Saya telah menambahkan pembuatan instance kelas jika diperlukan.
sumber
Saya baru saja menulis kelas util, itu termasuk metode tes, Anda dapat memiliki cek ~
IteratePackageUtil.java:
sumber
Solusi Aleksander Blomskøld tidak bekerja untuk saya untuk tes parameter
@RunWith(Parameterized.class)
ketika menggunakan Maven. Tes diberi nama dengan benar dan juga tempat ditemukan tetapi tidak dieksekusi:Masalah serupa telah dilaporkan di sini .
Dalam kasus saya
@Parameters
adalah membuat instance dari setiap kelas dalam sebuah paket. Tes bekerja dengan baik ketika dijalankan secara lokal di IDE. Namun, ketika menjalankan Maven tidak ada kelas di mana ditemukan dengan solusi Aleksander Blomskøld.Saya berhasil membuatnya dengan potongan berikut yang diilhami oleh komentar David Pärsson pada jawaban Aleksander Blomskøld:
sumber
Bagaimana dengan ini:
Anda kemudian dapat membebani fungsi:
Jika Anda perlu mengujinya:
Jika IDE Anda tidak memiliki pembantu impor:
Berhasil:
dari IDE Anda
untuk file JAR
tanpa ketergantungan eksternal
sumber
Hampir semua jawaban menggunakan
Reflections
atau membaca file kelas dari sistem file. Jika Anda mencoba membaca kelas dari sistem file, Anda mungkin mendapatkan kesalahan saat Anda mengemas aplikasi Anda sebagai JAR atau lainnya. Anda juga mungkin tidak ingin menggunakan perpustakaan terpisah untuk tujuan itu.Berikut ini adalah pendekatan lain yang murni java dan tidak tergantung pada sistem file.
Java 8 bukan keharusan . Anda dapat menggunakan loop sebagai ganti stream. Dan Anda bisa mengujinya seperti ini
sumber
ToolProvider.getSystemJavaCompiler()
, kode ini tidak memindai paket bersarang.Asalkan Anda tidak menggunakan loader kelas dinamis, Anda dapat mencari classpath dan untuk setiap entri mencari direktori atau file JAR.
sumber
Layak disebut
Jika Anda ingin memiliki daftar semua kelas di bawah beberapa paket, Anda dapat menggunakan
Reflection
cara berikut:Ini akan membuat daftar kelas yang nantinya bisa Anda gunakan sesuai keinginan.
sumber
Sangat mungkin, tetapi tanpa perpustakaan tambahan seperti
Reflections
itu sulit ...Ini sulit karena Anda belum memiliki instrumen lengkap untuk mendapatkan nama kelas.
Dan, saya mengambil kode
ClassFinder
kelas saya :sumber
Berdasarkan jawaban @ Staale , dan dalam upaya untuk tidak bergantung pada perpustakaan pihak ketiga, saya akan menerapkan pendekatan Sistem File dengan memeriksa lokasi fisik paket pertama dengan:
sumber
Jika Anda hanya ingin memuat grup kelas terkait, maka Spring dapat membantu Anda.
Spring dapat membuat daftar atau peta semua kelas yang mengimplementasikan antarmuka yang diberikan dalam satu baris kode. Daftar atau peta akan berisi contoh semua kelas yang mengimplementasikan antarmuka itu.
Yang sedang berkata, sebagai alternatif untuk memuat daftar kelas dari sistem file, alih-alih hanya mengimplementasikan antarmuka yang sama di semua kelas yang ingin Anda muat, terlepas dari paket dan gunakan Spring untuk memberi Anda contoh dari semuanya. Dengan begitu, Anda dapat memuat (dan instantiate) semua kelas yang Anda inginkan terlepas dari paket apa yang mereka masuki.
Di sisi lain, jika memiliki semuanya dalam satu paket adalah yang Anda inginkan, maka cukup sediakan semua kelas dalam paket tersebut untuk mengimplementasikan antarmuka yang diberikan.
sumber
java polos: FindAllClassesUsingPlainJavaReflectionTest.java
PS: untuk menyederhanakan pelat boiler untuk menangani kesalahan, dll, saya gunakan di sini
vavr
danlombok
perpustakaanimplementasi lain dapat ditemukan di repo GitHub daggerok / java-reflection-find-annotated-class-or-methods saya
sumber
Saya tidak dapat menemukan pekerjaan pendek untuk sesuatu yang sangat sederhana. Jadi ini dia, saya membuatnya sendiri setelah bermain-main sebentar:
sumber
Jika Anda berada di Spring-land, Anda dapat menggunakan
PathMatchingResourcePatternResolver
;sumber
Itu tidak mungkin, karena semua kelas dalam paket mungkin tidak dimuat, sementara Anda selalu tahu paket kelas.
sumber