JavaRush /Java Blog /Random-ID /Proxy Dinamis di Java

Proxy Dinamis di Java

Dipublikasikan di grup Random-ID
Halo! Hari ini kita akan melihat topik yang cukup penting dan menarik - membuat kelas proxy dinamis di Java. Ini tidak terlalu sederhana, jadi mari kita coba memahaminya dengan contoh :) Jadi, pertanyaan paling penting: apa itu proxy dinamis dan untuk apa? Kelas proxy adalah semacam “superstruktur” di atas kelas asli, yang memungkinkan kita mengubah perilakunya jika perlu. Apa yang dimaksud dengan mengubah perilaku dan bagaimana cara kerjanya? Mari kita lihat contoh sederhana. Katakanlah kita memiliki antarmuka Persondan kelas sederhana Manyang mengimplementasikan antarmuka ini
public interface Person {

   public void introduce(String name);

   public void sayAge(int age);

   public void sayFrom(String city, String country);
}

public class Man implements Person {

   private String name;
   private int age;
   private String city;
   private String country;

   public Man(String name, int age, String city, String country) {
       this.name = name;
       this.age = age;
       this.city = city;
       this.country = country;
   }

   @Override
   public void introduce(String name) {

       System.out.println("Меня зовут " + this.name);
   }

   @Override
   public void sayAge(int age) {
       System.out.println("Мне " + this.age + " years");
   }

   @Override
   public void sayFrom(String city, String country) {

       System.out.println("Я из города " + this.city + ", " + this.country);
   }

   //..геттеры, сеттеры, и т.д.
}
Kelas kami Manmemiliki 3 metode: perkenalkan diri Anda, sebutkan umur Anda, dan sebutkan dari mana Anda berasal. Bayangkan kita menerima kelas ini sebagai bagian dari perpustakaan JAR yang sudah jadi dan tidak bisa begitu saja mengambil dan menulis ulang kodenya. Namun, kita perlu mengubah perilakunya. Misalnya, kita tidak tahu metode mana yang akan dipanggil pada objek kita, dan oleh karena itu kita ingin orang tersebut terlebih dahulu mengatakan “Halo!” ketika memanggil salah satu dari mereka. (tidak ada yang menyukai seseorang yang tidak sopan). Proksi dinamis - 1Apa yang harus kita lakukan dalam situasi seperti ini? Kami memerlukan beberapa hal:
  1. InvocationHandler

Apa itu? Secara harfiah dapat diterjemahkan sebagai “pencegat panggilan”. Ini menggambarkan tujuannya dengan cukup akurat. InvocationHandleradalah antarmuka khusus yang memungkinkan kita mencegat pemanggilan metode apa pun ke objek kita dan menambahkan perilaku tambahan yang kita perlukan. Kita perlu membuat interseptor sendiri - yaitu membuat kelas dan mengimplementasikan antarmuka ini. Ini cukup sederhana:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class PersonInvocationHandler implements InvocationHandler {

private Person person;

public PersonInvocationHandler(Person person) {
   this.person = person;
}

 @Override
   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

       System.out.println("Hello!");
       return null;
   }
}
Kita hanya perlu mengimplementasikan satu metode antarmuka - invoke(). Faktanya, ia melakukan apa yang kita perlukan - ia memotong semua pemanggilan metode ke objek kita dan menambahkan perilaku yang diperlukan (di sini kita invoke()mencetak “Halo!” ke konsol di dalam metode).
  1. Objek asli dan proksinya.
Mari kita buat objek asli Mandan "superstruktur" (proxy) untuknya:
import java.lang.reflect.Proxy;

public class Main {

   public static void main(String[] args) {

       //Создаем оригинальный an object
       Man vasia = new Man("Vasya", 30, "Санкт-Петербург", "Россия");

       //Получаем загрузчик класса у оригинального an object
       ClassLoader vasiaClassLoader = vasia.getClass().getClassLoader();

       //Получаем все интерфейсы, которые реализует оригинальный an object
       Class[] interfaces = vasia.getClass().getInterfaces();

       //Создаем прокси нашего an object vasia
       Person proxyVasia = (Person) Proxy.newProxyInstance(vasiaClassLoader, interfaces, new PersonInvocationHandler(vasia));

       //Вызываем у прокси an object один из методов нашего оригинального an object
       proxyVasia.introduce(vasia.getName());

   }
}
Kelihatannya tidak terlalu sederhana! Saya secara khusus menulis komentar untuk setiap baris kode: mari kita lihat lebih dekat apa yang terjadi di sana.

Pada baris pertama kita cukup membuat objek asli yang akan kita buat proxynya. Dua baris berikut mungkin membuat Anda bingung:
//Получаем загрузчик класса у оригинального an object
ClassLoader vasiaClassLoader = vasia.getClass().getClassLoader();

//Получаем все интерфейсы, которые реализует оригинальный an object
Class[] interfaces = vasia.getClass().getInterfaces();
Tapi sebenarnya tidak ada hal istimewa yang terjadi di sini :) Untuk membuat proxy, kita memerlukan ClassLoader(pemuat kelas) dari objek asli dan daftar semua antarmuka yang Mandiimplementasikan oleh kelas asli kita (yaitu ). Jika Anda tidak tahu apa itu ClassLoader, Anda dapat membaca artikel tentang memuat kelas ke dalam JVM atau artikel ini di Habré , tetapi jangan terlalu memikirkannya dulu. Ingatlah bahwa kita mendapatkan sedikit informasi tambahan yang kemudian kita perlukan untuk membuat objek proxy. Pada baris keempat kita menggunakan kelas khusus Proxydan metode statisnya newProxyInstance():
//Создаем прокси нашего an object vasia
Person proxyVasia = (Person) Proxy.newProxyInstance(vasiaClassLoader, interfaces, new PersonInvocationHandler(vasia));
Metode ini baru saja membuat objek proxy kita. Ke metode ini kami meneruskan informasi tentang kelas asli yang kami terima pada langkah sebelumnya (itu ClassLoaderdan daftar antarmukanya), serta objek pencegat yang kami buat sebelumnya - InvocationHandler'a. Hal utama adalah jangan lupa untuk meneruskan objek asli kita ke interseptor vasia, jika tidak maka tidak akan ada yang bisa "dicegat" :) Apa yang kita dapatkan? Kami sekarang memiliki objek proxy vasiaProxy. Itu dapat memanggil metode antarmuka apa punPerson . Mengapa? Karena kami memberikannya daftar semua antarmuka - di sini:
//Получаем все интерфейсы, которые реализует оригинальный an object
Class[] interfaces = vasia.getClass().getInterfaces();

//Создаем прокси нашего an object vasia
Person proxyVasia = (Person) Proxy.newProxyInstance(vasiaClassLoader, interfaces, new PersonInvocationHandler(vasia));
Sekarang dia "sadar" akan semua metode antarmuka Person. Selain itu, kami memberikan proxy kami sebuah objek PersonInvocationHandleryang dikonfigurasi untuk bekerja dengan objek tersebut vasia:
//Создаем прокси нашего an object vasia
Person proxyVasia = (Person) Proxy.newProxyInstance(vasiaClassLoader, interfaces, new PersonInvocationHandler(vasia));
Sekarang, jika kita memanggil metode antarmuka apa pun pada objek proxy Person, pencegat kita akan “menangkap” panggilan ini dan sebagai gantinya mengeksekusi metodenya sendiri invoke(). Yuk coba jalankan caranya main()! Keluaran konsol: Halo! Besar! Kita melihat bahwa alih-alih metode sebenarnya, metode kita Person.introduce()disebut : invoke()PersonInvocationHandler()
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

   System.out.println("Hello!");
   return null;
}
Dan konsol menampilkan “Halo!” Tapi ini bukan perilaku yang kita inginkan :/ Menurut ide kita, “Halo!” pertama-tama harus ditampilkan, dan kemudian metode yang kita panggil harus berfungsi. Dengan kata lain, metode ini memanggil:
proxyVasia.introduce(vasia.getName());
harus menampilkan ke konsol “Halo! Nama saya Vasya,” dan bukan hanya “Halo!” Bagaimana kita bisa mencapai hal ini? Tidak ada yang rumit: Anda hanya perlu sedikit mengotak-atik interseptor dan metode kami invoke():) Perhatikan argumen apa yang diteruskan ke metode ini:
public Object invoke(Object proxy, Method method, Object[] args)
Suatu metode invoke()memiliki akses ke metode yang dipanggilnya dan semua argumennya (metode Metode, Objek[] args). Dengan kata lain, jika kita memanggil sebuah metode proxyVasia.introduce(vasia.getName()), dan alih-alih sebuah metode , introduce()sebuah metode dipanggil invoke(), di dalam metode tersebut kita memiliki akses ke metode asli introduce()dan argumennya! Hasilnya, kita bisa melakukan sesuatu seperti ini:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class PersonInvocationHandler implements InvocationHandler {

   private Person person;

   public PersonInvocationHandler(Person person) {

       this.person = person;
   }

   @Override
   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
       System.out.println("Hello!");
       return method.invoke(person, args);
   }
}
Sekarang kami telah menambahkan invoke()panggilan ke metode asli ke metode tersebut. Jika sekarang kita mencoba menjalankan kode dari contoh sebelumnya:
import java.lang.reflect.Proxy;

public class Main {

   public static void main(String[] args) {

       //Создаем оригинальный an object
       Man vasia = new Man("Vasya", 30, "Санкт-Петербург", "Россия");

       //Получаем загрузчик класса у оригинального an object
       ClassLoader vasiaClassLoader = vasia.getClass().getClassLoader();

       //Получаем все интерфейсы, которые реализует оригинальный an object
       Class[] interfaces = vasia.getClass().getInterfaces();

       //Создаем прокси нашего an object vasia
       Person proxyVasia = (Person) Proxy.newProxyInstance(vasiaClassLoader, interfaces, new PersonInvocationHandler(vasia));

       //Вызываем у прокси an object один из методов нашего оригинального an object
       proxyVasia.introduce(vasia.getName());
   }
}
maka kita akan melihat bahwa sekarang semuanya berfungsi sebagaimana mestinya :) Output konsol: Halo! Nama saya Vasya, di mana Anda membutuhkannya? Sebenarnya banyak tempat. Pola desain "proksi dinamis" secara aktif digunakan dalam teknologi populer... dan omong-omong, saya lupa memberi tahu Anda bahwa Dynamic Proxyitu adalah sebuah pola ! Selamat, Anda telah mempelajari satu sama lain! :) Proksi dinamis - 2Jadi, ini secara aktif digunakan dalam teknologi dan kerangka kerja populer yang berkaitan dengan keamanan. Bayangkan Anda memiliki 20 metode yang hanya dapat dijalankan oleh pengguna program Anda yang login. Dengan menggunakan teknik yang telah Anda pelajari, Anda dapat dengan mudah menambahkan pemeriksaan ke 20 metode ini untuk melihat apakah pengguna telah memasukkan login dan kata sandi, tanpa menduplikasi kode verifikasi secara terpisah di setiap metode. Atau, misalnya, jika Anda ingin membuat log yang akan mencatat semua tindakan pengguna, hal ini juga mudah dilakukan menggunakan proxy. Anda bahkan bisa sekarang: cukup tambahkan kode ke contoh sehingga nama metode ditampilkan di konsol saat dipanggil invoke(), dan Anda akan mendapatkan log sederhana dari program kami :) Di akhir kuliah, perhatikan satu hal penting batasan . Pembuatan objek proxy terjadi pada tingkat antarmuka, bukan pada tingkat kelas. Proksi dibuat untuk antarmuka. Lihatlah kode ini:
//Создаем прокси нашего an object vasia
Person proxyVasia = (Person) Proxy.newProxyInstance(vasiaClassLoader, interfaces, new PersonInvocationHandler(vasia));
Di sini kita membuat proxy khusus untuk antarmuka Person. Jika kita mencoba membuat proxy untuk kelas tersebut, yaitu, kita mengubah jenis tautan dan mencoba mentransmisikan ke kelas tersebut Man, tidak ada yang berhasil.
Man proxyVasia = (Man) Proxy.newProxyInstance(vasiaClassLoader, interfaces, new PersonInvocationHandler(vasia));

proxyVasia.introduce(vasia.getName());
Pengecualian di thread "utama" java.lang.ClassCastException: com.sun.proxy.$Proxy0 tidak dapat dilemparkan ke Man Kehadiran antarmuka merupakan persyaratan wajib. Proksi berfungsi pada tingkat antarmuka. Sekian untuk hari ini :) Sebagai materi tambahan tentang topik proxy, saya dapat merekomendasikan Anda video yang sangat bagus dan juga artikel yang bagus . Nah, sekarang alangkah baiknya jika kita menyelesaikan beberapa masalah! :) Sampai jumpa!
Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION