JavaRush /Java blogi /Random-UZ /Java-da dinamik proksi-serverlar

Java-da dinamik proksi-serverlar

Guruhda nashr etilgan
Salom! Bugun biz juda muhim va qiziqarli mavzuni ko'rib chiqamiz - Java-da dinamik proksi-klasslarni yaratish. Bu juda oddiy emas, shuning uchun buni misollar bilan tushunishga harakat qilaylik :) Shunday qilib, eng muhim savol: dinamik proksi-serverlar nima va ular nima uchun? Proksi-klass - bu asl sinfga nisbatan o'ziga xos "ustki tuzilma" bo'lib, agar kerak bo'lsa, uning xatti-harakatlarini o'zgartirishga imkon beradi. Xulq-atvorni o'zgartirish nimani anglatadi va u qanday ishlaydi? Keling, oddiy misolni ko'rib chiqaylik. Aytaylik, bizda interfeys va ushbu interfeysni amalga oshiradigan Personoddiy sinf mavjudMan
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);
   }

   //..геттеры, сеттеры, и т.д.
}
Sinfimizda Man3 ta usul mavjud: o'zingizni tanishtiring, yoshingizni ayting va qayerdan ekanligingizni ayting. Tasavvur qilaylik, biz bu sinfni tayyor JAR kutubxonasining bir qismi sifatida oldik va uning kodini shunchaki olib, qayta yoza olmaymiz. Biroq, biz uning xatti-harakatlarini o'zgartirishimiz kerak. Misol uchun, biz ob'ektimizga qaysi usul chaqirilishini bilmaymiz va shuning uchun biz ulardan biron biriga qo'ng'iroq qilganda odam birinchi navbatda "Salom!" Deyishini xohlaymiz. (odobsiz odamni hech kim yoqtirmaydi). Dinamik proksi-serverlar - 1Bunday vaziyatda nima qilishimiz kerak? Bizga bir nechta narsa kerak bo'ladi:
  1. InvocationHandler

Bu nima? Uni so'zma-so'z "qo'ng'iroqni ushlab turuvchi" deb tarjima qilish mumkin. Bu uning maqsadini juda aniq tasvirlaydi. InvocationHandlerob'ektimizga har qanday usul qo'ng'iroqlarini ushlab turish va bizga kerak bo'lgan qo'shimcha xatti-harakatlarni qo'shish imkonini beruvchi maxsus interfeys. Biz o'zimizning interceptorimizni yaratishimiz kerak - ya'ni sinf yaratish va ushbu interfeysni amalga oshirish. Bu juda oddiy:
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;
   }
}
Biz faqat bitta interfeys usulini amalga oshirishimiz kerak - invoke(). U, aslida, bizga kerak bo'lgan narsani qiladi - u ob'ektimizga barcha usul qo'ng'iroqlarini to'xtatadi va kerakli xatti-harakatni qo'shadi (bu erda biz invoke()usul ichidagi konsolga “Salom!” ni chop qilamiz).
  1. Asl ob'ekt va uning proksi.
Keling, asl ob'ekt Manva uning uchun "ustki tuzilma" (proksi) yarataylik:
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());

   }
}
Juda oddiy ko'rinmaydi! Men har bir kod qatori uchun alohida izoh yozdim: keling, u erda nima sodir bo'layotganini batafsil ko'rib chiqaylik.

Birinchi qatorda biz proksi-server yaratadigan asl ob'ektni yaratamiz. Quyidagi ikkita qator sizni chalkashtirib yuborishi mumkin:
//Получаем загрузчик класса у оригинального an object
ClassLoader vasiaClassLoader = vasia.getClass().getClassLoader();

//Получаем все интерфейсы, которые реализует оригинальный an object
Class[] interfaces = vasia.getClass().getInterfaces();
Lekin bu erda hech qanday maxsus narsa bo'lmaydi :) Proksi-server yaratish uchun bizga ClassLoaderasl ob'ektning (sinf yuklagichi) va bizning asl sinfimiz (ya'ni Man) amalga oshiradigan barcha interfeyslar ro'yxati kerak bo'ladi. Agar bu nima ekanligini bilmasangiz , JVM yoki Habré-da darslarni yuklash haqida ushbu maqolaniClassLoader o'qishingiz mumkin , lekin u bilan juda ko'p bezovta qilmang. Esda tutingki, biz biroz qo'shimcha ma'lumot olamiz, keyin proksi-server ob'ektini yaratishimiz kerak bo'ladi. To'rtinchi qatorda biz maxsus sinf va uning statik usulidan foydalanamiz : ProxynewProxyInstance()
//Создаем прокси нашего an object vasia
Person proxyVasia = (Person) Proxy.newProxyInstance(vasiaClassLoader, interfaces, new PersonInvocationHandler(vasia));
Bu usul bizning proksi-serverimizni yaratadi. Usulga biz oldingi bosqichda olingan asl sinf (u ClassLoaderva uning interfeyslari ro'yxati), shuningdek, biz ilgari yaratgan interceptor ob'ekti haqidagi ma'lumotlarni o'tkazamiz - InvocationHandler'a. Asosiysi, asl ob'ektimizni interceptorga o'tkazishni unutmang vasia, aks holda "to'xtatib turish" uchun hech narsa bo'lmaydi :) Biz nima bilan yakun topdik? Endi bizda proksi obyekti bor vasiaProxy. U har qanday interfeys usullariniPerson chaqirishi mumkin . Nega? Chunki biz unga barcha interfeyslar ro'yxatini berdik - bu erda:
//Получаем все интерфейсы, которые реализует оригинальный an object
Class[] interfaces = vasia.getClass().getInterfaces();

//Создаем прокси нашего an object vasia
Person proxyVasia = (Person) Proxy.newProxyInstance(vasiaClassLoader, interfaces, new PersonInvocationHandler(vasia));
Endi u barcha interfeys usullaridan "xabardor" Person. PersonInvocationHandlerBundan tashqari, biz proksi-serverimizga ob'ekt bilan ishlash uchun sozlangan ob'ektni topshirdik vasia:
//Создаем прокси нашего an object vasia
Person proxyVasia = (Person) Proxy.newProxyInstance(vasiaClassLoader, interfaces, new PersonInvocationHandler(vasia));
Endi, agar biz proksi-obyektda istalgan interfeys usulini chaqirsak Person, bizning interceptorimiz ushbu qo'ng'iroqni "ushlaydi" va o'rniga o'z usulini bajaradi invoke(). Keling, usulni ishga tushirishga harakat qilaylik main()! Konsol chiqishi: Salom! Ajoyib! Ko'ramizki, haqiqiy usul o'rniga bizning Person.introduce()usulimiz deyiladi : invoke()PersonInvocationHandler()
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

   System.out.println("Hello!");
   return null;
}
Va konsolda "Salom!" Ammo bu biz olishni xohlagan xatti-harakat emas:/ Bizning fikrimizga ko'ra, avval "Salom!" Ko'rsatilishi kerak, keyin esa biz chaqirayotgan usulning o'zi ishlashi kerak. Boshqacha qilib aytganda, bu usul chaqiradi:
proxyVasia.introduce(vasia.getName());
konsolga chiqishi kerak “Salom! Mening ismim Vasya, nafaqat "Salom!" Bunga qanday erishishimiz mumkin? Hech qanday murakkab narsa yo'q: siz bizning to'xtatuvchimiz va usulimiz bilan biroz o'ylashingiz kerak invoke():) Ushbu usulga qanday argumentlar berilganiga e'tibor bering:
public Object invoke(Object proxy, Method method, Object[] args)
Usul invoke()o'rniga chaqirilgan usulga va uning barcha argumentlariga kirish huquqiga ega (Metod usuli, Object[] args). Boshqacha qilib aytadigan bo'lsak, agar biz usulni chaqirsak proxyVasia.introduce(vasia.getName())va usul o'rniga introduce()usul chaqiriladi invoke(), bu usul ichida biz asl usul va uning argumentiga kirishimiz mumkin introduce()! Natijada, biz shunday qila olamiz:
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);
   }
}
invoke()Endi biz usulga asl usulga qo'ng'iroqni qo'shdik . Agar biz oldingi misolimizdagi kodni ishga tushirishga harakat qilsak:
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());
   }
}
keyin biz hozir hamma narsa kerakli darajada ishlayotganini ko'ramiz :) Konsol chiqishi: Salom! Mening ismim Vasya, qayerda kerak bo'lishi mumkin? Aslida, ko'p joylar. "Dinamik proksi" dizayn namunasi mashhur texnologiyalarda faol qo'llaniladi ... va aytmoqchi, Dynamic Proxybu naqsh ekanligini aytishni unutibman ! Tabriklaymiz, siz yana bir narsani o'rgandingiz! :) Dinamik proksi-serverlar - 2Shunday qilib, u xavfsizlik bilan bog'liq mashhur texnologiyalar va ramkalarda faol qo'llaniladi. Tasavvur qiling-a, sizda 20 ta usul bor, ularni faqat dasturingizga kirgan foydalanuvchilar bajarishi mumkin. O'zingiz o'rgangan usullardan foydalanib, ushbu 20 ta usulga foydalanuvchi login va parolni kiritgan-qilmaganligini tekshirish uchun tekshirishni osongina qo'shishingiz mumkin, har bir usulda tasdiqlash kodini alohida takrorlamasdan. Yoki, masalan, foydalanuvchining barcha harakatlari yozib olinadigan jurnal yaratmoqchi bo'lsangiz, buni proksi-server yordamida qilish ham oson. Siz hozir ham qila olasiz: shunchaki misolga kod qo'shing, shunda usul nomi chaqirilganda konsolda ko'rsatiladi invoke()va siz bizning dasturimizning oddiy jurnalini olasiz :) Ma'ruza oxirida bitta muhim narsaga e'tibor bering . cheklash . Proksi-server ob'ektini yaratish sinf darajasida emas, balki interfeys darajasida sodir bo'ladi. Interfeys uchun proksi-server yaratiladi. Ushbu kodni ko'rib chiqing:
//Создаем прокси нашего an object vasia
Person proxyVasia = (Person) Proxy.newProxyInstance(vasiaClassLoader, interfaces, new PersonInvocationHandler(vasia));
Bu erda biz interfeys uchun maxsus proksi yaratamiz Person. Agar biz sinf uchun proksi-server yaratishga harakat qilsak, ya'ni havola turini o'zgartiramiz va sinfga uzatishga harakat qilsak Man, hech narsa ishlamaydi.
Man proxyVasia = (Man) Proxy.newProxyInstance(vasiaClassLoader, interfaces, new PersonInvocationHandler(vasia));

proxyVasia.introduce(vasia.getName());
"Main" java.lang.ClassCastException: com.sun.proxy.$Proxy0 dagi istisno. Manga o'tkazib bo'lmaydi. Interfeysning mavjudligi majburiy talabdir. Proksi interfeys darajasida ishlaydi. Bugun hammasi shu :) Proksi-serverlar mavzusi bo'yicha qo'shimcha material sifatida sizga ajoyib video va yaxshi maqolani tavsiya qilishim mumkin . Xo'sh, endi bir nechta muammolarni hal qilish yaxshi bo'lardi! :) Ko'rishguncha!
Izohlar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION