Pertanyaan Bagaimana memaksa Java untuk memuat kelas setelah instantiasi?


Latar Belakang:
Saya memiliki kelas MainTest yang memiliki banyak tombol, yang masing-masing instantiate kelas yang saya coding / pengujian. Saya ingin kode / siklus pengujian untuk kelas-kelas ini menjadi cepat, dan melihat pengaruh perubahan saya dengan cepat, beberapa kali dalam satu menit. MainTest yang stabil membutuhkan waktu sekitar 20 detik untuk dimuat, yang tidak akan menjadi masalah jika saya tidak perlu memuat ulang untuk setiap perubahan dalam kelas yang diinformasikannya. Saya ingin memuat MainTest sekali, dan ketika instantiates kelas lain, sebut saja ChildTest, berkali-kali (saat event tombol), itu harus memuat versi terbaru ChildTest.

Pertanyaannya singkat:
Bagaimana Anda memberi tahu perintah java 'baru' untuk memuat kembali kelas dari disk dan bukan dari cache jvm?


Saya mencoba Class.ForName tetapi itu tidak membuat perbedaan.
Saya juga mencoba menggunakan classloader kustom (disalin dari open source), tidak berhasil.


17
2017-10-19 18:39


asal


Jawaban:


Tidak ada harapan "overloading" new operator tetapi Anda pasti bisa menulis loader kelas kustom yang hanya memuat ulang kode byte setiap kali Anda memintanya untuk memuat kelas. Tidak ada classloader out-of-the-box akan melakukan apa yang Anda cari karena mereka semua berasumsi bahwa definisi kelas tidak akan berubah melalui kehidupan JVM.

Tapi inilah cara Anda mewujudkannya. Buat loader kelas yang disebut, katakan, Reloader yang mengesampingkan metode loadClass dan findClass metode sehingga mereka hanya reload file kelas dari disk setiap kali mereka dipanggil (bukan "caching" mereka untuk digunakan nanti). Maka Anda hanya perlu menelepon new Reloader().loadClass("foo.bar.MyClassName") setiap kali Anda mencurigai definisi kelas telah berubah (misalnya sebagai bagian dari metode siklus hidup kerangka pengujian Anda).

Artikel ini mengisi beberapa detail tetapi melewatkan beberapa poin penting, terutama tentang menggunakan instance baru dari classloader untuk pemuatan ulang berikutnya dan mendelegasikannya ke classloader default bila sesuai. Berikut ini adalah contoh kerja sederhana yang berulang kali memuat kelas MyClass dan mengasumsikan file kelasnya ada di direktori "./bin" relatif:

public class Reloader extends ClassLoader {
    public static void main(String[] args) throws Exception {
        do {
            Object foo = new Reloader().loadClass("MyFoo").newInstance();
            System.out.println("LOADED: " + foo); // Overload MyFoo#toString() for effect
            System.out.println("Press <ENTER> when MyFoo.class has changed");
            System.in.read();
        } while (true);
    }

    @Override
    public Class<?> loadClass(String s) {
        return findClass(s);
    }

    @Override
    public Class<?> findClass(String s) {
        try {
            byte[] bytes = loadClassData(s);
            return defineClass(s, bytes, 0, bytes.length);
        } catch (IOException ioe) {
            try {
                return super.loadClass(s);
            } catch (ClassNotFoundException ignore) { }
            ioe.printStackTrace(System.out);
            return null;
        }
    }

    private byte[] loadClassData(String className) throws IOException {
        File f = new File("bin/" + className.replaceAll("\\.", "/") + ".class");
        int size = (int) f.length();
        byte buff[] = new byte[size];
        FileInputStream fis = new FileInputStream(f);
        DataInputStream dis = new DataInputStream(fis);
        dis.readFully(buff);
        dis.close();
        return buff;
    }
}

Pada setiap pemanggilan blok "lakukan / sementara" dalam metode utama, Reloader baru dipakai yang memuat kelas dari disk dan mengembalikannya ke pemanggil. Jadi jika Anda menimpa bin/MyClass.class file berisi implementasi baru dengan yang berbeda, kelebihan beban toString metode, maka Anda harus melihat implementasi baru setiap kali.


26
2017-10-19 19:07



Kedengarannya agak menakutkan, tetapi ini akan membantu.

http://download.oracle.com/javase/1.4.2/docs/api/java/lang/ClassLoader.html

ClassLoader dapat secara dinamis memuat kelas pada saat runtime, saya akan membaca api untuk menentukan apakah memuatnya lagi menimpa versi sebelumnya.


1
2017-10-19 18:52



Saya menemukan artikel tentang masalah ini.
http://www.zeroturnaround.com/blog/reloading-objects-classes-classloaders/

TETAPI, jawaban maerik juga terlihat bagus. akan mencobanya nanti.


1
2017-10-19 20:19



Kedengarannya seperti Anda ingin menggunakan penyebaran panas ketika dapat digunakan dengan debugger. Ketika Anda mendebug masalah dan merekondisi beberapa kelasnya, Anda bisa mendapatkan opsi untuk memuat kembali kelas yang diubah.

EDIT: Selain menggunakan API debugging, Anda dapat menggunakan Instrumentasi. http://download-llnw.oracle.com/javase/6/docs/api/java/lang/instrument/package-summary.html

Namun, karena menggunakan debugger adalah cara termudah untuk melakukan ini, jika ini tidak berhasil, Anda cenderung mengalami masalah yang sama.

Kedengarannya seperti apa yang Anda butuhkan untuk menguji karya yang lebih kecil sehingga dibutuhkan kurang dari satu detik menjalankan beberapa bagian dari aplikasi Anda.

Atau Anda dapat memuat aplikasi Anda lebih cepat dengan menyediakan fasilitas dump dan re-load untuk memori. Dengan cara ini Anda dapat memulai aplikasi Anda dari snapshot (mungkin segera)


1
2017-10-19 18:50



Anda bisa mencoba Java Rebel. Ini adalah loader kelas "khusus pengembangan" yang dirancang untuk melakukan apa yang Anda butuhkan, tetapi mungkin itu mahal untuk kebutuhan Anda. Dalam kasus Anda, solusi Peter Lawrey bisa cukup.


1
2017-10-20 09:47



Saya tidak yakin bagaimana melakukan ini dalam kode, tetapi NetBeans memiliki tombol "Terapkan Perubahan Kode" yang akan melakukan ini.

Namun, itu hanya dapat mengubah implementasi kelas, itu tidak dapat mengubah tanda tangannya. Ini berarti Anda tidak dapat menambah, menghapus, atau mengubah variabel atau metode instan. Ini karena desain JVM yang tidak memungkinkan ini diubah begitu kelas telah dimuat sekali.

Karena NetBeans dapat melakukannya, pasti ada jalan, tetapi saya tidak tahu cara melakukannya secara manual.

Jika Anda menjalankan IDE yang mendukung fitur ini, Anda cukup mengeklik tombol setiap kali Anda memodifikasi kelas. Kemudian akan mengkompilasi ulang kelas itu dan memuatnya kembali.


0
2017-10-19 18:50