Berbagi teknologi

Penjelasan rinci tentang proksi: proksi statis, proksi dinamis, implementasi Spring AOP

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

1. Pengenalan agen

Proxy mengacu pada mode di mana objek A dapat memiliki perilaku yang sama seperti B dengan menahan objek B lainnya. Untuk membuka protokol ke dunia luar, B sering mengimplementasikan antarmuka, dan A juga akan mengimplementasikan antarmuka tersebut. Tapi B adalah kelas implementasi "nyata", sedangkan A lebih "virtual". Ia meminjam metode B untuk mengimplementasikan metode antarmuka. Meskipun A adalah "tentara semu", ia dapat meningkatkan B dan melakukan hal lain sebelum dan sesudah memanggil metode B. Spring AOP menggunakan proxy dinamis untuk menyelesaikan "penenunan" kode dinamis.

Manfaat menggunakan proxy lebih dari itu. Jika suatu proyek bergantung pada antarmuka yang disediakan oleh proyek lain, tetapi antarmuka proyek lain tidak stabil dan protokolnya sering berubah, Anda dapat menggunakan proxy ketika antarmuka berubah hanya perlu memodifikasi proxy, tidak satu per satu. Dalam hal ini, kita dapat melakukan ini untuk semua antarmuka yang menyesuaikan dengan dunia luar untuk mencegah kode luar menyerang kode kita. Ini disebut pemrograman defensif. Mungkin masih banyak aplikasi lain untuk proxy.

Dalam contoh di atas, kelas A dikodekan secara keras untuk menampung B, yang merupakan proksi statis dari B. Jika objek proksi A tidak pasti, maka proksi tersebut adalah proksi dinamis. Saat ini ada dua implementasi umum dari proksi dinamis, proksi dinamis jdk dan proksi dinamis cglib.

2. Proksi statis

Proksi statis adalah pola desain dan jenis pola proksi. Dalam proxy statis, kelas proxy telah ditentukan sebelum program dijalankan, dan hubungan antara kelas proxy dan kelas proxy ditentukan pada waktu kompilasi. Ini berarti bahwa kelas proksi dan kelas proksi mengimplementasikan antarmuka yang sama atau mewarisi kelas induk yang sama. Kelas proksi secara internal menyimpan turunan dari kelas proksi dan memanggil metode kelas proksi dalam metodenya sendiri , itu dapat Tambahkan beberapa operasi Anda sendiri sebelum dan sesudah panggilan, seperti pencatatan, pemeriksaan izin, pemrosesan transaksi, dll.

Proksi statis memiliki tiga komponen: antarmuka abstrak, kelas proksi, dan kelas proksi.

1) Tentukan antarmuka abstrak

  1. public interface TargetInteface {
  2. void method1();
  3. void method2();
  4. int method3(Integer i);
  5. }

2) Tentukan kelas proxy

  1. public class TargetProxy implements TargetInteface {
  2. private Target target =new Target();
  3. @Override
  4. public void method1() {
  5. System.out.println("执行方法前...");
  6. target.method1();
  7. System.out.println("执行方法后...");
  8. }
  9. @Override
  10. public void method2() {
  11. System.out.println("执行方法前...");
  12. target.method2();
  13. System.out.println("执行方法后...");
  14. }
  15. @Override
  16. public int method3(Integer i) {
  17. System.out.println("执行方法前...");
  18. int method3 = target.method3(i);
  19. System.out.println("执行方法后...");
  20. return method3;
  21. }
  22. }

3) Tentukan kelas proxy

  1. public class Target implements TargetInteface {
  2. @Override
  3. public void method1() {
  4. System.out.println(" Target method1 running ...");
  5. }
  6. @Override
  7. public void method2() {
  8. System.out.println("Target method2 running ...");
  9. }
  10. @Override
  11. public int method3(Integer i) {
  12. System.out.println("Target method3 running ...");
  13. return i;
  14. }
  15. }

4) Tentukan klien dan lihat hasil eksekusi

  1. public class TargetUser {
  2. public static void main(String[] args) {
  3. TargetInteface target = new TargetProxy();
  4. target.method1();
  5. System.out.println("-----------------------------");
  6. target.method2();
  7. System.out.println("-----------------------------");
  8. System.out.println(target.method3(3));
  9. }
  10. }

Keluaran hasil:

Sebelum menjalankan metode...
Metode target 1 sedang berjalan...
Setelah menjalankan metode...
-----------------------------
Sebelum menjalankan metode...
Metode target2 sedang berjalan...
Setelah menjalankan metode...
-----------------------------
Sebelum menjalankan metode...
Metode target 3 berjalan ...
Setelah menjalankan metode...
3

Tidak sulit untuk melihat dari implementasi agen statis bahwa kelebihan agen statis adalah implementasinya yang sederhana dan mudah dipahami. Namun kekurangannya juga terlihat jelas, yaitu, setiap kali Anda perlu menambahkan fungsionalitas proxy ke kelas baru, Anda perlu membuat kelas proxy baru secara manual, yang akan menyebabkan peningkatan tajam dalam jumlah kelas dan peningkatan biaya pemeliharaan. . Pada saat yang sama, tingkat penggabungan antara kelas proksi dan kelas proksi terlalu tinggi. Ketika metode ditambahkan, dihapus, atau diubah di kelas proksi, metode yang sesuai juga harus ditambahkan, dihapus, atau diubah di kelas proksi. , yang meningkatkan biaya pemeliharaan kode. Masalah lainnya adalah ketika objek proxy memproksi kelas implementasi dari beberapa antarmuka target, harus ada metode yang berbeda di beberapa kelas implementasi, karena objek proxy harus mengimplementasikan antarmuka yang sama dengan objek target (sebenarnya merupakan hubungan penyertaan), maka harus ditulis Banyak metode yang dapat dengan mudah menyebabkan kode membengkak dan sulit dipelihara.

3. Proksi dinamis

Ide inti dari proxy dinamis adalah untuk mengakses objek asli secara tidak langsung melalui objek proxy tanpa mengubah kode objek asli, dan melakukan operasi tambahan sebelum dan sesudah akses.

Prinsip implementasi proksi dinamis terutama didasarkan pada mekanisme refleksi Java. Saat menggunakan proxy dinamis, Anda perlu mendefinisikan antarmuka atau sekumpulan antarmuka yang menentukan perilaku kelas yang diproksi (objek yang diproksi).Kemudian, Anda perlu menulis implementasinyaInvocationHandler Kelas antarmuka, kelas ini berisi logika yang dieksekusi sebelum dan sesudah pemanggilan metode pada objek proxy.saat meneleponProxy.newProxyInstance()metode, meneruskan pemuat kelas antarmuka, array antarmuka danInvocationHandlerObyek, Java secara dinamis akan menghasilkan kelas proxy yang mengimplementasikan antarmuka yang ditentukan saat runtime, dan mendelegasikan pemanggilan metodeInvocationHandler objek yang akan diproses.Ketika suatu metode objek proksi dipanggil, ia benar-benar memanggilInvocationHandlerAntarmukainvoke()Metode, di mana Anda dapat melakukan beberapa logika pra-pemrosesan berdasarkan nama metode, parameter, dan informasi lainnya, lalu memanggil metode yang sesuai dari objek proxy melalui refleksi.

Selanjutnya, kami memperkenalkan dua proxy dinamis: JDK Proxy dan CGLib

1)Proksi JDK

① Mekanisme internal Proxy JDK

JDK Proxy menggunakan mekanisme refleksi Java untuk menghasilkan kelas proxy secara dinamis. Secara khusus,Proxykelas akan digunakanProxyGenerator kelas (walaupun kelas ini bukan API publik, ini adalah kunci untuk mengimplementasikan proksi dinamis di dalam JDK) untuk menghasilkan bytecode kelas proksi dan memuatnya ke dalam JVM.Kelas proxy yang dihasilkan akan diwarisijava.lang.reflect.Proxy kelas dan mengimplementasikan antarmuka yang ditentukan.Dalam metode kelas proxy, itu akan dipanggilInvocationHandlerdariinvokeMetode, meneruskan panggilan metode ke prosesor untuk diproses.

Selain itu, untuk meningkatkan kinerja, JDK Proxy juga menyediakan mekanisme caching untuk melakukan caching objek Kelas dari kelas proxy yang dihasilkan. Dengan cara ini, ketika Anda perlu membuat objek proxy dengan tipe yang sama, Anda bisa langsung mendapatkan objek Kelas dari kelas proxy dari cache tanpa membuatnya ulang.Caching dilakukan melaluiWeakCacheDiimplementasikan oleh kelas, ini menggunakan referensi lemah ke objek cache sehingga item cache yang tidak lagi digunakan dapat dibersihkan secara otomatis saat JVM melakukan pengumpulan sampah.

② Langkah-langkah implementasi Proxy JDK

  • Tentukan antarmuka dan kelas proksi: Pertama-tama tentukan satu atau lebih antarmuka, yang akan diimplementasikan oleh kelas proxy.
  1. public interface TargetInteface {
  2. void method1();
  3. void method2();
  4. int method3(Integer i);
  5. }
  1. public class Target implements TargetInteface {
  2. @Override
  3. public void method1() {
  4. System.out.println("method1 running ...");
  5. }
  6. @Override
  7. public void method2() {
  8. System.out.println("method2 running ...");
  9. }
  10. @Override
  11. public int method3(Integer i) {
  12. System.out.println("method3 running ...");
  13. return i;
  14. }
  15. }
  • BuatInvocationHandler:menyelesaikanInvocationHandlerantarmuka dan menulis ulanginvoke metode.adainvokeDalam metode ini, Anda dapat menambahkan logika khusus, seperti logging, pemeriksaan izin, dll., dan memanggil metode kelas asli melalui refleksi.
  • Hasilkan objek proxy:transferProxy.newProxyInstancemetode, meneruskan pemuat kelas, array antarmuka danInvocationHandler Contoh untuk menghasilkan objek proxy secara dinamis. Metode ini mengembalikan contoh kelas proksi yang mengimplementasikan antarmuka yang ditentukan.
  1. public class TargetProxy {
  2. public static <T> Object getTarget(T t) {
  3. //新构建了一个 新的 代理类的对象
  4. return Proxy.newProxyInstance(t.getClass().getClassLoader(), t.getClass().getInterfaces(), new InvocationHandler() {
  5. @Override
  6. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  7. // proxy就是目标对象t,method就是调用目标对象中方法,args就是调用目标对象中方法的参数。
  8. //比如说:代理对象.method1(),这时proxy就是目标类,method1就是method,args就是method1方法参数。
  9. System.out.println("执行方法前...");
  10. Object invoke = method.invoke(t, args);
  11. System.out.println("执行方法后...");
  12. return invoke;
  13. }
  14. });
  15. }
  16. }
  • Gunakan objek proxy: Ketika suatu metode dipanggil melalui objek proxy, metode tersebut sebenarnya dipanggilInvocationHandlerdariinvokeMetode, di mana logika kustom dijalankan dan kemudian metode kelas asli dipanggil.
  1. public class TargetUser {
  2. public static void main(String[] args) {
  3. TargetInteface target = (TargetInteface) TargetProxy.getTarget(new Target());
  4. target.method1();
  5. System.out.println("-----------------------------");
  6. target.method2();
  7. System.out.println("-----------------------------");
  8. System.out.println(target.method3(3));
  9. }
  10. }

Keluaran hasil:
Sebelum menjalankan metode...
metode1 menjalankan ...
Setelah menjalankan metode...
-----------------------------
Sebelum menjalankan metode...
metode2 menjalankan ...
Setelah menjalankan metode...
-----------------------------
Sebelum menjalankan metode...
metode3 menjalankan ...
Setelah menjalankan metode...
3

③ Fitur Proksi JDK

  1. Proksi antarmuka: JDK Proxy hanya dapat memproksi kelas yang mengimplementasikan antarmuka, dan tidak dapat memproksi kelas biasa yang tidak mengimplementasikan antarmuka.
  2. Dihasilkan secara dinamis: Kelas proksi dihasilkan secara dinamis saat runtime, dan pengembang tidak perlu menulis kode untuk kelas proksi secara manual.
  3. Fleksibilitas tinggi: Fungsionalitas atau logika tambahan dapat ditambahkan ke kelas asli tanpa mengubah kode kelas asli.
  4. Pertimbangan kinerja: Karena keterlibatan refleksi dan pembuatan kelas dinamis, kinerja Proxy JDK mungkin sedikit lebih rendah dibandingkan proxy statis atau panggilan langsung ke metode kelas asli.

2) CGLib

① Prinsip inti proksi dinamis CGLib

  1. Operasi bytecode : CGLib menggunakan ASM (kerangka manipulasi bytecode yang kecil dan cepat) untuk secara dinamis menghasilkan kelas Java baru (biasanya subkelas dari kelas target). Kelas-kelas yang baru dihasilkan ini mewarisi dari kelas target dan menyisipkan logika proksi ketika metode ini dipanggil.
  2. Intersepsi metode : Fungsi inti CGLib adalah mengimplementasikan intersepsi tingkat metode.Pengembang menerapkanMethodInterceptorantarmuka untuk menentukan pencegat metode, yang akan menjalankan logika khusus sebelum dan sesudah pemanggilan metode objek proksi, seperti prapemrosesan, pascapemrosesan, penanganan pengecualian, dll.
  3. Mekanisme Kelas Cepat : Untuk meningkatkan kinerja, CGLib mengadopsi mekanisme FastClass. FastClass mengindeks metode kelas target dan mengakses metode target secara langsung melalui indeks saat memanggil. Metode ini jauh lebih cepat daripada refleksi Java.

② Langkah-langkah implementasi proksi dinamis CGLib

  • Memperkenalkan dependensi CGLib:Memperkenalkan ketergantungan Maven atau Gradle CGLib ke dalam proyek.
  1. import net.sf.cglib.proxy.Enhancer;
  2. import net.sf.cglib.proxy.MethodInterceptor;
  3. import net.sf.cglib.proxy.MethodProxy;
  4. import java.lang.reflect.Method;
  • Tentukan kelas sasaran: Tentukan kelas target yang perlu diproksi.
  1. public class Target {
  2. public void method1() {
  3. System.out.println("method1 running ...");
  4. }
  5. public void method2() {
  6. System.out.println("method2 running ...");
  7. }
  8. public int method3(Integer i) {
  9. System.out.println("method3 running ...");
  10. return i;
  11. }
  12. }
  • Implementasikan antarmuka MethodInterceptor:Buat implementasiMethodInterceptorkelas antarmuka dan overrideintercept metode. Tulis logika proxy dalam metode ini.
  • Buat objek proxy: Gunakan yang disediakan oleh CGLibEnhancer kelas untuk membuat objek proxy.Anda perlu mengatur kelas yang diproksi (viasetSuperclassmetode) dan panggilan balik (viasetCallbackMetode menetapkan kelas implementasi MethodInterceptor).
  1. public class TargetProxy {
  2. public static <T> Object getProxy(T t) {
  3. Enhancer en = new Enhancer(); //帮我们生成代理对象
  4. en.setSuperclass(t.getClass());//设置要代理的目标类
  5. en.setCallback(new MethodInterceptor() {//代理要做什么
  6. @Override
  7. public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
  8. System.out.println("执行方法前。。。");
  9. //调用原有方法
  10. Object invoke = methodProxy.invokeSuper(object, args);
  11. // Object invoke = method.invoke(t, args);// 作用等同与上面。
  12. System.out.println("执行方法后。。。");
  13. return invoke;
  14. }
  15. });
  16. return en.create();
  17. }
  18. }
  • Gunakan objek proxy: Dipicu ketika metode kelas target dipanggil melalui objek proxyinterceptLogika proxy dalam metode.
  1. public class TargetUser {
  2. public static void main(String[] args) {
  3. Target target = (Target) TargetProxy.getProxy(new Target());
  4. System.out.println(target.getClass().getName());
  5. target.method1();
  6. }
  7. }

Keluaran hasil:

com.heaboy.aopdemo.cglibproxy.TargetBahasa InggrisNHANCBahasa Inggris:RBkamuCGSayaSAYABf9f41fb8
sebelum menjalankan metode tersebut. . .
metode1 menjalankan ...
Setelah menjalankan metode tersebut. . .

③ Skenario yang berlaku untuk proksi dinamis CGLib

  1. Perlu kelas proxy yang tidak mengimplementasikan antarmuka: Ketika kelas target tidak mengimplementasikan antarmuka apa pun, CGLib dapat digunakan sebagai proxy.
  2. Persyaratan kinerja tinggi: Dalam skenario dengan persyaratan kinerja tinggi, jika proksi dinamis JDK tidak dapat memenuhi kebutuhan, Anda dapat mempertimbangkan untuk menggunakan CGLib.
  3. Implementasi kerangka AOP: Dalam kerangka pemrograman berorientasi aspek, seperti Spring AOP, ketika kelas yang tidak mengimplementasikan antarmuka perlu diproksi, CGLib biasanya digunakan sebagai implementasi yang mendasarinya.

④ Kelebihan dan kekurangan proxy dinamis CGLib

keuntungan:

  1. Fleksibilitas tinggi: Bisakah kelas proxy yang tidak mengimplementasikan antarmuka, memperluas cakupan penerapan proxy.
  2. Performa lebih baik: Melalui mekanisme FastClass, efisiensi pemanggilan lebih tinggi daripada mekanisme refleksi proxy dinamis JDK.
  3. Kuat: Mendukung penambahan fungsionalitas atau logika tambahan secara dinamis ke kelas target saat runtime tanpa mengubah kode kelas asli.

kekurangan:

  1. Overhead operasi bytecode: Menghasilkan bytecode secara dinamis dan memuatnya ke JVM akan membawa overhead kinerja tertentu.
  2. Tidak dapat memproksi kelas dan metode akhir: Karena CGLib mengimplementasikan proksi dengan mewarisi kelas target, CGLib tidak dapat memproksi kelas dan metode yang dimodifikasi akhir.
  3. Lebih kompleks untuk digunakan: Dibandingkan dengan proksi dinamis JDK, CGLib lebih kompleks untuk digunakan dan perlu memperkenalkan dependensi tambahan dan menangani masalah pembuatan bytecode.

3) Proksi JDK vs CGLib

Jenis objek proxy:JDK Proxy hanya dapat memproksi kelas yang mengimplementasikan antarmuka; sedangkan CGLib dapat secara langsung memproksi kelas biasa.

pertunjukan: CGLib menghasilkan subkelas kelas proxy saat runtime dan umumnya dianggap berkinerja sedikit lebih baik daripada JDK Proxy. Namun dalam sebagian besar skenario, perbedaan kinerja ini tidak signifikan.

adegan yang akan digunakan: Jika objek target sudah mengimplementasikan antarmuka, menggunakan JDK Proxy adalah pilihan yang sederhana dan mudah. Jika Anda perlu mem-proxy kelas yang tidak mengimplementasikan antarmuka, Anda harus menggunakan CGLib.

mengandalkan:JDK Proxy tidak memerlukan dependensi tambahan karena merupakan bagian dari pustaka inti Java, sedangkan CGLib memerlukan penambahan pustaka CGLib sebagai dependensi proyek.

JDK Proxy adalah fungsi yang hadir dengan bahasa Java dan tidak perlu diimplementasikan dengan memuat kelas pihak ketiga;

Java memberikan dukungan yang stabil untuk JDK Proxy, dan akan terus meningkatkan dan memperbarui JDK Proxy. Misalnya, kinerja JDK Proxy di versi Java 8 telah meningkat pesat dibandingkan versi sebelumnya;

Proxy JDK diimplementasikan melalui interseptor dan refleksi;

JDK Proxy hanya dapat memproksi kelas yang mewarisi antarmuka;

JDK Proxy relatif mudah diimplementasikan dan dipanggil;

CGLib merupakan alat yang disediakan oleh pihak ketiga, diimplementasikan berdasarkan ASM, dan memiliki kinerja yang relatif tinggi;

CGLib tidak perlu diimplementasikan melalui antarmuka, melainkan disebut dengan mengimplementasikan subkelas.

4. Proksi statis VS proksi dinamis

perbedaan

Proksi statis: Proksi statis ditentukan selama kompilasi. Kelas proksi perlu ditulis untuk setiap kelas proksi dan kelas proksi mengimplementasikan antarmuka yang sama atau mewarisi kelas induk yang sama.

Kelas proksi dari proksi statis ada pada waktu kompilasi, sehingga hanya dapat memproksi kelas tertentu saat program sedang berjalan, dan tidak dapat secara dinamis memutuskan kelas mana yang akan diproksi.

Proksi statis membungkus pemanggilan metode objek asli dan dapat menambahkan logika tambahan sebelum dan sesudah panggilan, tetapi kelas proksi perlu ditulis terlebih dahulu, yang akan menambah jumlah kode.

Proksi statis secara eksplisit menentukan objek proksi dalam kode dan relatif intuitif untuk digunakan, tetapi penambahan kelas proksi baru memerlukan kompilasi ulang.

Proksi dinamis: Proksi dinamis membuat objek proksi saat runtime tanpa menulis kelas proksi terlebih dahulu. Gunakan mekanisme refleksi Java untuk menghasilkan kelas proksi dan objek proksi secara dinamis.

Proksi dinamis didasarkan pada antarmuka dan diimplementasikan melalui kelas java.lang.reflect.Proxy dan antarmuka java.lang.reflect.InvocationHandler.

Proksi dinamis dapat memproksi kelas dari beberapa antarmuka dan secara dinamis memutuskan kelas mana yang akan diproksi. Saat runtime, proxy dapat dibuat untuk objek berbeda sesuai kebutuhan, sehingga memberikan fleksibilitas yang lebih besar.

Proksi dinamis tidak perlu menulis kelas proksi tertentu untuk setiap kelas proksi, yang lebih fleksibel dan menghemat kode.

Agen dinamis dapat menambahkan logika khusus sebelum dan sesudah pemanggilan metode pada objek proksi, seperti pembuatan log, manajemen transaksi, dll. Kerugian dari proksi dinamis adalah dibandingkan dengan proksi statis, menghasilkan objek proksi saat runtime memerlukan overhead kinerja tertentu.

Adegan yang berlaku

Proksi statis cocok untuk skenario berikut:

Ketika jumlah objek target (objek proxy) dibatasi dan ditentukan, proxy statis dapat diimplementasikan dengan menulis kelas proxy secara manual. Proksi statis membuat kelas proksi pada waktu kompilasi, sehingga memiliki kinerja yang lebih baik pada waktu proses.

Proksi statis merangkum objek target dan menambahkan fungsi tambahan tanpa mengubah kode aslinya. Hal ini membuat proxy statis sering digunakan untuk masalah lintas sektoral seperti logging dan manajemen transaksi.

Proksi dinamis cocok untuk skenario berikut:

Ketika jumlah objek target tidak pasti atau tidak dapat ditentukan sebelumnya, proxy dinamis dapat dengan lebih mudah menghasilkan objek proxy. Ini menghasilkan kelas proxy dan objek proxy saat runtime, menghindari pekerjaan yang membosankan dalam menulis beberapa kelas proxy secara manual.

Proksi dinamis memberikan fleksibilitas untuk menambah, menghapus, atau mengubah perilaku proksi untuk objek target saat runtime. Hal ini membuat proxy dinamis sering digunakan dalam skenario aplikasi seperti AOP (pemrograman berorientasi aspek) dan RPC (panggilan prosedur jarak jauh).

Perlu dicatat bahwa karena proxy dinamis membuat kelas proxy dan objek proxy melalui mekanisme refleksi saat runtime, kinerjanya mungkin sedikit lebih rendah dibandingkan proxy statis. Selain itu, proksi dinamis hanya dapat memproksi objek target yang mengimplementasikan antarmuka, sedangkan proksi statis tidak memiliki batasan ini.

Singkatnya, proxy statis cocok untuk skenario di mana jumlah objek target terbatas dan pasti, serta memerlukan enkapsulasi dan fungsi tambahan; sedangkan proxy dinamis cocok untuk skenario di mana jumlah objek target tidak pasti atau tidak dapat ditentukan sebelumnya. dan perilaku proxy perlu ditambahkan, dihapus, atau diubah secara fleksibel. Pilih metode agensi yang sesuai berdasarkan kebutuhan dan keadaan tertentu.

5. Implementasi proxy di SpringAOP

1) Pengantar SpringAOP

Bicara tentang pengertian AOP

Spring AOP adalah modul penting dalam kerangka Spring, yang digunakan untuk mengimplementasikan pemrograman berorientasi aspek.

Pemrograman tatap muka , ini adalah model pemrograman yang memungkinkan pemrogram untuk melakukan modularisasi melalui titik-titik lintas sektoral khusus dan merangkum perilaku yang memengaruhi beberapa kelas ke dalam modul yang dapat digunakan kembali. Contoh: Misalnya, keluaran log, jika Anda tidak menggunakan AOP, Anda perlu meletakkan pernyataan keluaran log di semua kelas dan metode. Namun, dengan AOP, Anda dapat merangkum pernyataan keluaran log ke dalam modul yang dapat digunakan kembali dan menempatkannya di a secara deklaratif. Dalam suatu kelas, keluaran log secara otomatis diselesaikan setiap kali kelas tersebut digunakan.

Dalam ide pemrograman berorientasi aspek, fungsi dibagi menjadi dua jenis

  • Bisnis inti: Login, registrasi, penambahan, penghapusan, modifikasi, dan query semuanya disebut bisnis inti

  • Fungsi periferal: Log dan manajemen transaksi adalah layanan periferal sekunder.

Dalam pemrograman berorientasi aspek, fungsi bisnis inti dan fungsi periferal dikembangkan secara independen, dan keduanya tidak digabungkan. Kemudian fungsi aspek dan fungsi bisnis inti “dijalin” menjadi satu, yang disebut AOP.

AOP dapat mengonversi yang tidak terkait dengan bisnis,Tapi itu dikemas untuk logika atau tanggung jawab (seperti pemrosesan transaksi, manajemen log, kontrol izin, dll.) yang biasa disebut dengan modul bisnis.,mudah untukMengurangi kode duplikat pada sistemKurangi kopling antar modul,DanKondusif untuk skalabilitas dan pemeliharaan di masa depan

Ada konsep berikut dalam AOP:

  • AspekJ: Aspek hanyalah sebuah konsep. Tidak ada antarmuka atau kelas khusus yang terkait dengannya. Ini adalah nama kolektif untuk Titik Gabung, Saran, dan Pointcut.

  • Bergabunglah dengan titik : Titik koneksi mengacu pada titik selama eksekusi program, seperti pemanggilan metode, penanganan pengecualian, dll. Di Spring AOP, hanya titik gabungan tingkat metode yang didukung.

  • Nasihat : Notifikasi, yaitu logika lintas sektoral dalam suatu aspek yang kita definisikan, memiliki tiga jenis: "sekitar", "sebelum" dan "sesudah". Dalam banyak kerangka implementasi AOP, Advice biasanya bertindak sebagai interseptor, dan juga dapat berisi banyak interseptor sebagai tautan untuk diproses di sekitar titik Gabung.

  • Titik potong: Pointcut, digunakan untuk mencocokkan titik gabungan. Poin Gabung mana yang terdapat dalam AspectJ perlu difilter oleh Pointcut.

  • Perkenalan : Pendahuluan, memungkinkan suatu aspek untuk mendeklarasikan bahwa objek yang disarankan mengimplementasikan antarmuka tambahan apa pun yang sebenarnya tidak diimplementasikannya. Misalnya, objek proxy dapat digunakan untuk memproksi dua kelas target.

  • Menenun : Weaving, sekarang kita memiliki titik koneksi, titik potong, notifikasi dan aspek, bagaimana cara menerapkannya ke program? Benar sekali, ini tenun. Di bawah panduan pointcuts, logika notifikasi dimasukkan ke dalam metode target, sehingga logika notifikasi kita dapat dijalankan saat metode tersebut dipanggil.

  • Proksi AOP : Proksi AOP mengacu pada objek yang mengimplementasikan protokol aspek dalam kerangka implementasi AOP. Ada dua macam proxy di Spring AOP, yaitu proxy dinamis JDK dan proxy dinamis CGLIB.

  • Objek sasaran: Objek target adalah objek yang diproksi.

Spring AOP diimplementasikan berdasarkan proksi dinamis JDK dan promosi Cglib. Kedua metode proksi tersebut merupakan metode runtime, sehingga tidak memiliki pemrosesan waktu kompilasi, sehingga Spring diimplementasikan melalui kode Java.

Masalah apa yang dipecahkan oleh AOP?

Beberapa perilaku umum yang tersebar di beberapa kelas atau objek (seperti logging, manajemen transaksi, kontrol izin, pembatasan arus antarmuka, kekuatan antarmuka, dll.), perilaku ini biasanya disebut kekhawatiran lintas sektoral . Jika kita menerapkan perilaku ini berulang kali di setiap kelas atau objek, hal ini akan menyebabkan kode menjadi mubazir, rumit, dan sulit dipelihara.

AOP dapat mengubah masalah lintas sektoral (seperti logging, manajemen transaksi, kontrol izin, pembatasan arus antarmuka, kekuatan antarmuka, dll.) dari Logika bisnis inti (keprihatinan inti, keprihatinan inti) Pisahkan dari fokus untuk mencapai pemisahan kekhawatiran.

Skenario aplikasi AOP

  • Logging: Sesuaikan anotasi logging dan gunakan AOP untuk mencapai logging dengan satu baris kode.

  • Statistik kinerja: Gunakan AOP untuk menghitung waktu eksekusi metode sebelum dan sesudah eksekusi metode target untuk memfasilitasi pengoptimalan dan analisis.

  • Pengelolaan transaksi:@Transactional Anotasi memungkinkan Spring melakukan manajemen transaksi untuk kami, seperti mengembalikan operasi pengecualian, menghilangkan logika manajemen transaksi yang berulang.@TransactionalAnotasi diterapkan berdasarkan AOP.

  • Kontrol izin: Gunakan AOP untuk menentukan apakah pengguna memiliki izin yang diperlukan sebelum menjalankan metode target. Jika demikian, jalankan metode target, jika tidak maka metode target tidak akan dijalankan.Misalnya, SpringSecurity menggunakan@PreAuthorize Anda dapat menyesuaikan verifikasi izin dengan mengomentari baris kode.

  • Pembatasan arus antarmuka: Gunakan AOP untuk membatasi permintaan melalui algoritma dan implementasi pembatasan arus tertentu sebelum metode target dijalankan.

  • Manajemen cache: Gunakan AOP untuk membaca dan memperbarui cache sebelum dan sesudah metode target dijalankan.

Bagaimana AOP diimplementasikan

Metode implementasi umum AOP mencakup proksi dinamis, operasi bytecode, dll.

Spring AOP didasarkan pada proksi dinamis. Jika objek yang akan diproksi mengimplementasikan antarmuka tertentu, maka Spring AOP akan digunakan Proksi JDK, untuk membuat objek proxy. Untuk objek yang tidak mengimplementasikan antarmuka, Anda tidak dapat menggunakan Proxy JDK untuk proksi Bahasa Inggris Cglib Hasilkan subkelas dari objek yang diproksi untuk dijadikan sebagai proksi

2) Menerapkan SpringAOP berdasarkan proksi dinamis JDK Proxy

① Konfigurasikan SpringAOP

adaspring-aop.xmlKonfigurasikan kacang dan aspek terkait dalam file konfigurasi

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
  5. <bean id="target" class="com.xxhh.aopdemo.aop.Target"/>
  6. <bean id="targetAdvice" class="com.xxhh.aopdemo.aop.TargetAdvice"/>
  7. <bean id="targetProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
  8. <property name="target" ref="target"/> <!--被代理的类-->
  9. <property name="interceptorNames" value="targetAdvice"/> <!--如果用多种增强方式,value的值使用逗号(,)分割-->
  10. <property name="proxyTargetClass" value="false"/> <!--如果设置为true,则创建基于类的代理(使用CGLIB);如果设置为false,则创建基于接口的代理(使用JDK动态代理)。-->
  11. <property name="interfaces" value="com.xxhh.aopdemo.aop.TargetInteface"/> <!--target实现的接口-->
  12. </bean>
  13. </beans>

② Tentukan antarmuka abstrak

  1. public interface TargetInteface {
  2. void method1();
  3. void method2();
  4. int method3(Integer i);
  5. }

③ Tentukan kelas proxy

  1. public class Target implements TargetInteface{
  2. /*
  3. * 需要增强的方法,连接点JoinPoint
  4. **/
  5. @Override
  6. public void method1() {
  7. System.out.println("method1 running ...");
  8. }
  9. @Override
  10. public void method2() {
  11. System.out.println("method2 running ...");
  12. }
  13. @Override
  14. public int method3(Integer i) {
  15. System.out.println("method3 running ...");
  16. return i;
  17. }
  18. }

④ Tentukan kelas proxy (metode peningkatan)

  1. public class TargetAdvice implements MethodInterceptor, MethodBeforeAdvice, AfterReturningAdvice {
  2. /*
  3. * 通知/增强
  4. **/
  5. @Override
  6. public Object invoke(MethodInvocation methodInvocation) throws Throwable {
  7. System.out.println("前置环绕通知");
  8. Object proceed = methodInvocation.proceed();
  9. System.out.println("后置环绕通知");
  10. return proceed;
  11. }
  12. @Override
  13. public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
  14. System.out.println("后置返回通知");
  15. }
  16. @Override
  17. public void before(Method method, Object[] args, Object target) throws Throwable {
  18. System.out.println("前置通知");
  19. }
  20. }

⑤ Tes

  1. public class AopTest {
  2. public static void main(String[] args) {
  3. ApplicationContext appCtx = new ClassPathXmlApplicationContext("spring-aop.xml");
  4. TargetInteface targetProxy = (TargetInteface) appCtx.getBean("targetProxy");
  5. targetProxy.method1();
  6. }
  7. }

Hasil keluaran:

Kelilingi notifikasi depan
Pra-pemberitahuan
metode1 menjalankan ...
pemberitahuan pasca pengembalian
Pemberitahuan keliling kembali

3) Menerapkan SpringAOP berdasarkan proksi dinamis CGLib

① Konfigurasikan SpringAOP

adaspring-confaop.xmlKonfigurasikan kacang dan aspek terkait dalam file konfigurasi

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xmlns:aop="http://www.springframework.org/schema/aop"
  6. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
  7. <!--先开启cglib代理,开启 exposeProxy = true,暴露代理对象-->
  8. <aop:aspectj-autoproxy proxy-target-class="true" expose-proxy="true"/>
  9. <!--扫包-->
  10. <context:component-scan base-package="com.xxhh.aopdemo.annotationaop"/>
  11. </beans>

② Tentukan kelas proxy

  1. /*
  2. * 目标类
  3. **/
  4. public class Target {
  5. public void method1() {
  6. System.out.println("method1 running ...");
  7. }
  8. public void method2() {
  9. System.out.println("method2 running ...");
  10. }
  11. /*
  12. * 连接点JoinPoint
  13. **/
  14. public int method3(Integer i) {
  15. System.out.println("method3 running ...");
  16. // int i1 = 1 / i;
  17. return i;
  18. }
  19. }

③ Tentukan kelas proxy (kelas aspek)

  1. import org.aspectj.lang.ProceedingJoinPoint;
  2. /*
  3. * 切面类
  4. **/
  5. public class TargetAspect {
  6. /*
  7. * 前置通知
  8. **/
  9. public void before() {
  10. System.out.println("conf前置通知");
  11. }
  12. public void after() {
  13. System.out.println("conf后置通知");
  14. }
  15. public void afterReturning() {
  16. System.out.println("conf后置返回通知");
  17. }
  18. public void afterThrowing(Exception ex) throws Exception {
  19. // System.out.println("conf异常通知");
  20. // System.out.println(ex.getMessage());
  21. }
  22. public Object around(ProceedingJoinPoint pjp) throws Throwable {
  23. Object proceed = null;
  24. if (!"".equals("admin")) {
  25. System.out.println("conf环绕前置");
  26. proceed = pjp.proceed(pjp.getArgs());
  27. System.out.println("conf环绕后置");
  28. }
  29. return proceed;
  30. }
  31. }

④ Tes

  1. public class AopTest {
  2. public static void main(String[] args) {
  3. ApplicationContext appCtx = new ClassPathXmlApplicationContext("spring-confaop.xml");
  4. Target targetProxy = (Target) appCtx.getBean("target");
  5. System.out.println(targetProxy.method3(0));
  6. }
  7. }

Hasil keluaran:

conf pra-pemberitahuan
awalan surround conf
metode3 menjalankan ...
conf pemberitahuan pasca pengembalian
conf surround posting
pemberitahuan posting conf

4) Menerapkan SpringAOP berdasarkan proksi dinamis anotasi

① Konfigurasikan SpringAOP

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xmlns:aop="http://www.springframework.org/schema/aop"
  6. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
  7. <!--先开启cglib代理,开启 exposeProxy = true,暴露代理对象-->
  8. <aop:aspectj-autoproxy proxy-target-class="true" expose-proxy="true"/>
  9. <!--扫包-->
  10. <context:component-scan base-package="com.xxhh.aopdemo.annotationaop"/>
  11. </beans>

② Tentukan anotasi

  1. @Target(ElementType.METHOD)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. public @interface TestAnnotation{
  5. }

③ Tentukan kelas aspek

  1. /*
  2. * 切面类
  3. **/
  4. @Aspect
  5. @Component
  6. public class AnnotationAspect {
  7. // 定义一个切点:所有被RequestMapping注解修饰的方法会织入advice
  8. @Pointcut("@annotation(TestAnnotation)")
  9. private void advicePointcut() {}
  10. /*
  11. * 前置通知
  12. **/
  13. @Before("advicePointcut()")
  14. public void before() {
  15. System.out.println("annotation前置通知");
  16. }
  17. @After("advicePointcut()")
  18. public void after() {
  19. System.out.println("annotation后置通知");
  20. }
  21. @AfterReturning(pointcut = "advicePointcut()")
  22. public void afterReturning() {
  23. System.out.println("annotation后置返回通知");
  24. }
  25. @AfterThrowing(pointcut = "advicePointcut()", throwing = "ex")
  26. public void afterThrowing(Exception ex) throws Exception {
  27. System.out.println("annotation异常通知");
  28. System.out.println(ex.getMessage());
  29. }
  30. @Around("advicePointcut()")
  31. public Object around(ProceedingJoinPoint pjp) throws Throwable {
  32. Object proceed = null;
  33. if (!"".equals("admin")) {
  34. System.out.println("annotation环绕前置");
  35. proceed = pjp.proceed(pjp.getArgs());
  36. System.out.println("annotation环绕后置");
  37. }
  38. return proceed;
  39. }
  40. }

④ Pengontrol menambahkan anotasi

  1. @Controller
  2. public class TestController {
  3. @RequestMapping("/test.do")
  4. @ResponseBody
  5. public String testController() {
  6. TestController o = (TestController) AopContext.currentProxy();
  7. o.test();
  8. // System.out.println("tewt");
  9. return "ok";
  10. }
  11. @TestAnnotation
  12. public void test() {
  13. System.out.println("test running");
  14. }
  15. }

⑤ Tes