Pengenalan cglib

1. Gambaran keseluruhan

Dalam artikel ini, kita akan melihat perpustakaan cglib (Perpustakaan Penjanaan Kod). Ini adalah perpustakaan instrumentasi byte yang digunakan dalam banyak kerangka kerja Java seperti Hibernate atau Spring . Instrumentasi bytecode membolehkan memanipulasi atau membuat kelas selepas fasa penyusunan program.

2. Ketergantungan Maven

Untuk menggunakan cglib dalam projek anda, cukup tambahkan kebergantungan Maven (versi terbaru boleh didapati di sini):

 cglib cglib 3.2.4 

3. Cglib

Kelas di Java dimuat secara dinamik pada waktu runtime. Cglib menggunakan fitur bahasa Java ini untuk memungkinkan menambahkan kelas baru ke program Java yang sudah berjalan.

Hibernate menggunakan cglib untuk penghasilan proksi dinamik. Sebagai contoh, ia tidak akan mengembalikan objek penuh yang disimpan dalam pangkalan data tetapi akan mengembalikan versi kelas tersimpan yang diinstrumentasi yang dengan malas memuat nilai dari pangkalan data berdasarkan permintaan.

Kerangka ejekan yang popular, seperti Mockito, menggunakan cglib untuk kaedah mengejek. Mock adalah kelas instrumen di mana kaedah diganti dengan pelaksanaan kosong.

Kami akan melihat konstruk yang paling berguna dari cglib.

4. Melaksanakan Proksi Menggunakan cglib

Katakan bahawa kita mempunyai kelas PersonService yang mempunyai dua kaedah:

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

Perhatikan bahawa kaedah pertama mengembalikan String dan yang kedua Integer.

4.1. Mengembalikan Nilai Yang Sama

Kami ingin membuat kelas proksi mudah yang akan memintas kaedah panggilan ke sayHello () . The Enhancer kelas membolehkan kita untuk membuat proksi oleh dinamik melanjutkan PersonService kelas dengan menggunakan setSuperclass () kaedah dari Enhancer kelas:

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 antara muka panggil balik yang hanya mengembalikan nilai daripada kaedah yang diproksikan. Melaksanakan kaedah sayHello () pada proksi mengembalikan nilai yang ditentukan dalam kaedah proksi.

4.2. Nilai Pengembalian Bergantung pada Kaedah Tandatangan

Versi pertama proksi kami mempunyai beberapa kelemahan kerana kami tidak dapat memutuskan kaedah mana proksi harus dipintas, dan kaedah mana yang harus digunakan dari superclass. Kita boleh menggunakan antara muka MethodInterceptor untuk memintas semua panggilan ke proksi dan memutuskan sama ada mahu membuat panggilan tertentu atau melaksanakan kaedah 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 memintas semua panggilan apabila tandatangan kaedah bukan dari kelas Objek , yang bermaksud bahawa kaedah toString () atau hashCode () tidak akan dipintas. Selain itu, kami hanya memintas kaedah dari PersonService yang mengembalikan String . Kaedah panggilan ke lengthOfName () tidak akan dipintas kerana jenis pengembaliannya adalah Integer.

5. Pencipta Kacang

Satu lagi konstruk berguna dari cglib adalah kelas BeanGenerator . Ini membolehkan kita membuat kacang secara dinamik dan menambah ladang bersama dengan kaedah setter dan getter. Ia boleh digunakan oleh alat penjanaan kod 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

A mixin adalah membina yang membolehkan menggabungkan pelbagai objek ke dalam satu. Kita boleh memasukkan tingkah laku beberapa kelas dan memperlihatkan tingkah laku itu sebagai satu kelas atau antara muka. The cglib mixins membenarkan gabungan beberapa objek ke satu objek. Namun, untuk melakukannya semua objek yang termasuk dalam mixin mesti disokong oleh antara muka.

Katakan bahawa kita mahu membuat gabungan dua antara muka. Kita perlu menentukan kedua-dua antara muka dan pelaksanaannya:

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 menyusun pelaksanaan Interface1 dan Interface2, kita perlu membuat antara muka yang memperluas kedua-duanya:

public interface MixinInterface extends Interface1, Interface2 { }

Dengan menggunakan kaedah create () dari kelas Mixin kita dapat memasukkan tingkah laku Class1 dan Class2 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());

Kaedah memanggil pada mixinDelegate akan menggunakan pelaksanaan dari Class1 dan Class2.

7. Kesimpulannya

Dalam artikel ini, kami melihat cglib dan konstruknya yang paling berguna. Kami membuat proksi menggunakan kelas Enhancer . Kami menggunakan BeanCreator dan akhirnya, kami membuat Mixin yang merangkumi tingkah laku kelas lain.

Cglib digunakan secara meluas oleh rangka Spring. Salah satu contoh penggunaan proksi cglib oleh Spring ialah menambahkan kekangan keselamatan pada kaedah panggilan. Daripada memanggil kaedah secara langsung, keselamatan Spring akan terlebih dahulu memeriksa (melalui proksi) jika pemeriksaan keselamatan yang ditentukan berlalu dan menyerahkan kepada kaedah sebenarnya hanya jika pengesahan ini berjaya. Dalam artikel ini, kami melihat bagaimana membuat proksi sedemikian untuk tujuan kami sendiri.

Pelaksanaan semua contoh dan coretan kod ini terdapat dalam projek GitHub - ini adalah projek Maven, jadi mudah untuk diimport dan dijalankan sebagaimana adanya.