Pengantar cglib

1. Ikhtisar

Pada artikel ini, kita akan melihat pustaka cglib (Code Generation Library). Ini adalah pustaka instrumentasi byte yang digunakan di banyak framework Java seperti Hibernate atau Spring . Instrumentasi bytecode memungkinkan manipulasi atau pembuatan kelas setelah fase kompilasi program.

2. Ketergantungan Maven

Untuk menggunakan cglib dalam project Anda, cukup tambahkan dependensi Maven (versi terbaru dapat ditemukan di sini):

 cglib cglib 3.2.4 

3. Cglib

Kelas di Java dimuat secara dinamis saat runtime. Cglib menggunakan fitur bahasa Java ini untuk memungkinkan penambahan kelas baru ke program Java yang sudah berjalan.

Hibernate menggunakan cglib untuk menghasilkan proxy dinamis. Misalnya, ini tidak akan mengembalikan objek lengkap yang disimpan dalam database tetapi akan mengembalikan versi berinstrumen dari kelas tersimpan yang memuat nilai dari database sesuai permintaan dengan malas.

Kerangka kerja tiruan yang populer, seperti Mockito, menggunakan cglib untuk metode tiruan. Maket adalah kelas berinstrumen di mana metode diganti dengan implementasi kosong.

Kita akan melihat konstruksi yang paling berguna dari cglib.

4. Menerapkan Proxy Menggunakan cglib

Katakanlah kita memiliki kelas PersonService yang memiliki dua metode:

public class PersonService { public String sayHello(String name) { return "Hello " + name; } public Integer lengthOfName(String name) { return name.length(); } }

Perhatikan bahwa metode pertama mengembalikan String dan yang kedua Integer.

4.1. Mengembalikan Nilai yang Sama

Kami ingin membuat kelas proxy sederhana yang akan mencegat panggilan ke metode sayHello () . Kelas Enhancer memungkinkan kita untuk membuat proxy dengan memperluas kelas PersonService secara dinamis dengan menggunakan metode setSuperclass () dari kelas Enhancer :

Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(PersonService.class); enhancer.setCallback((FixedValue) () -> "Hello Tom!"); PersonService proxy = (PersonService) enhancer.create(); String res = proxy.sayHello(null); assertEquals("Hello Tom!", res);

The FixedValue adalah antarmuka callback yang hanya mengembalikan nilai dari metode proxy. Menjalankan metode sayHello () pada proxy mengembalikan nilai yang ditentukan dalam metode proxy.

4.2. Mengembalikan Nilai Bergantung pada Tanda Tangan Metode

Versi pertama dari proxy kami memiliki beberapa kelemahan karena kami tidak dapat memutuskan metode mana yang harus dicegat oleh proxy, dan metode mana yang harus dipanggil dari superclass. Kita dapat menggunakan antarmuka MethodInterceptor untuk mencegat semua panggilan ke proxy dan memutuskan apakah ingin membuat panggilan tertentu atau menjalankan metode dari superclass:

Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(PersonService.class); enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> { if (method.getDeclaringClass() != Object.class && method.getReturnType() == String.class) { return "Hello Tom!"; } else { return proxy.invokeSuper(obj, args); } }); PersonService proxy = (PersonService) enhancer.create(); assertEquals("Hello Tom!", proxy.sayHello(null)); int lengthOfName = proxy.lengthOfName("Mary"); assertEquals(4, lengthOfName);

Dalam contoh ini, kita mencegat semua panggilan saat tanda tangan metode bukan dari kelas Objek , artinya metode toString () atau hashCode () tidak akan dicegat. Selain itu, kami hanya mencegat metode dari PersonService yang mengembalikan String . Panggilan ke metode lengthOfName () tidak akan dicegat karena tipe kembaliannya adalah Integer.

5. Pembuat Kacang

Konstruksi lain yang berguna dari cglib adalah kelas BeanGenerator . Ini memungkinkan kita untuk membuat kacang secara dinamis dan menambahkan bidang bersama dengan metode penyetel dan pengambil. Ini dapat digunakan oleh alat pembuat kode untuk menghasilkan objek POJO sederhana:

BeanGenerator beanGenerator = new BeanGenerator(); beanGenerator.addProperty("name", String.class); Object myBean = beanGenerator.create(); Method setter = myBean.getClass().getMethod("setName", String.class); setter.invoke(myBean, "some string value set by a cglib"); Method getter = myBean.getClass().getMethod("getName"); assertEquals("some string value set by a cglib", getter.invoke(myBean));

6. Membuat Mixin

Sebuah mixin adalah membangun yang memungkinkan menggabungkan beberapa objek menjadi satu. Kita dapat menyertakan perilaku beberapa kelas dan mengekspos perilaku itu sebagai satu kelas atau antarmuka. The CGLIB mixin memungkinkan kombinasi dari beberapa objek ke objek tunggal. Namun, untuk melakukannya, semua objek yang disertakan dalam mixin harus didukung oleh antarmuka.

Katakanlah kita ingin membuat campuran dari dua antarmuka. Kita perlu mendefinisikan antarmuka dan implementasinya:

public interface Interface1 { String first(); } public interface Interface2 { String second(); } public class Class1 implements Interface1 { @Override public String first() { return "first behaviour"; } } public class Class2 implements Interface2 { @Override public String second() { return "second behaviour"; } } 

Untuk membuat implementasi Interface1 dan Interface2, kita perlu membuat antarmuka yang memperluas keduanya:

public interface MixinInterface extends Interface1, Interface2 { }

Dengan menggunakan metode create () dari kelas Mixin, kita dapat menyertakan perilaku Kelas1 dan Kelas2 ke dalam MixinInterface:

Mixin mixin = Mixin.create( new Class[]{ Interface1.class, Interface2.class, MixinInterface.class }, new Object[]{ new Class1(), new Class2() } ); MixinInterface mixinDelegate = (MixinInterface) mixin; assertEquals("first behaviour", mixinDelegate.first()); assertEquals("second behaviour", mixinDelegate.second());

Memanggil metode pada mixinDelegate akan memanggil implementasi dari Class1 dan Class2.

7. Kesimpulan

Pada artikel ini, kami melihat cglib dan konstruksinya yang paling berguna. Kami membuat proxy menggunakan kelas Enhancer . Kami menggunakan BeanCreator dan akhirnya, kami membuat Mixin yang menyertakan perilaku kelas lain.

Cglib digunakan secara luas oleh framework Spring. Salah satu contoh penggunaan proxy cglib oleh Spring adalah menambahkan batasan keamanan ke panggilan metode. Alih-alih memanggil metode secara langsung, keamanan Spring akan terlebih dahulu memeriksa (melalui proxy) jika pemeriksaan keamanan yang ditentukan lolos dan mendelegasikan ke metode sebenarnya hanya jika verifikasi ini berhasil. Pada artikel ini, kami melihat cara membuat proxy semacam itu untuk tujuan kami sendiri.

Penerapan semua contoh dan cuplikan kode ini dapat ditemukan di proyek GitHub - ini adalah proyek Maven, jadi semestinya mudah untuk mengimpor dan menjalankannya apa adanya.