JavaRush /Java Blogu /Random-AZ /Java-da Dinamik Proksilər

Java-da Dinamik Proksilər

Qrupda dərc edilmişdir
Salam! Bu gün biz olduqca vacib və maraqlı bir mövzuya baxacağıq - Java-da dinamik proxy siniflərinin yaradılması. Bu çox sadə deyil, buna görə də nümunələrlə anlamağa çalışaq :) Beləliklə, ən vacib sual: dinamik proksilər nədir və onlar nə üçündür? Proksi sinfi, lazım olduqda onun davranışını dəyişdirməyə imkan verən orijinal sinif üzərində bir növ "üst quruluşdur". Davranışı dəyişdirmək nə deməkdir və bu necə işləyir? Sadə bir misala baxaq. Tutaq ki, bizim interfeysimiz və bu interfeysi həyata keçirən Personsadə sinifimiz varMan
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);
   }

   //..геттеры, сеттеры, и т.д.
}
Sinifimizin Man3 üsulu var: özünü təqdim et, yaşını söylə və haradan olduğunu söylə. Təsəvvür edək ki, biz bu sinfi hazır JAR kitabxanasının bir hissəsi kimi almışıq və sadəcə onun kodunu götürüb yenidən yaza bilmirik. Bununla belə, onun davranışını dəyişdirməliyik. Məsələn, obyektimizdə hansı metodun çağırılacağını bilmirik və buna görə də onlardan hər hansı birinə zəng edərkən həmin şəxsin ilk olaraq “Salam!” deməsini istəyirik. (ədəbsiz olanı heç kim sevməz). Dinamik proksilər - 1Belə bir vəziyyətdə nə etməliyik? Bizə bir neçə şey lazım olacaq:
  1. InvocationHandler

Bu nədir? Hərfi mənada "zəngin qarşısını alan" kimi tərcümə edilə bilər. Bu, onun məqsədini olduqca dəqiq təsvir edir. InvocationHandlerobyektimizə istənilən metod çağırışlarını tutmağa və bizə lazım olan əlavə davranışı əlavə etməyə imkan verən xüsusi interfeysdir. Biz öz interceptorumuzu yaratmalıyıq - yəni bir sinif yaratmalı və bu interfeysi həyata keçirməliyik. Bu olduqca sadədir:
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;
   }
}
Yalnız bir interfeys metodunu tətbiq etməliyik - invoke(). O, əslində bizə lazım olanı edir - obyektimizə edilən bütün metod çağırışlarına müdaxilə edir və lazımi davranışı əlavə edir (burada invoke()metodun içərisindəki konsola “Salam!” çap edirik).
  1. Orijinal obyekt və onun proxy.
ManOrijinal obyekt və onun üçün “üst struktur” (proksi) yaradaq :
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());

   }
}
Çox sadə görünmür! Mən hər bir kod sətri üçün xüsusi olaraq şərh yazdım: gəlin orada baş verənlərə daha yaxından nəzər salaq.

Birinci sətirdə sadəcə olaraq proksi yaradacağımız orijinal obyekti yaradırıq. Aşağıdakı iki sətir çaşqınlığa səbəb ola bilər:
//Получаем загрузчик класса у оригинального an object
ClassLoader vasiaClassLoader = vasia.getClass().getClassLoader();

//Получаем все интерфейсы, которые реализует оригинальный an object
Class[] interfaces = vasia.getClass().getInterfaces();
Amma burada həqiqətən də xüsusi bir şey yoxdur :) Proksi yaratmaq üçün bizə ClassLoaderorijinal obyektin (sinif yükləyicisi) və orijinal sinifimizin (yəni Man) tətbiq etdiyi bütün interfeyslərin siyahısı lazımdır. Bunun nə olduğunu bilmirsinizsə , JVM-ə dərslərin yüklənməsi və ya Habré-də bu məqaləniClassLoader oxuya bilərsiniz , lakin bununla çox narahat olmayın. Unutmayın ki, biz bir az əlavə məlumat alırıq ki, sonra proxy obyekti yaratmalıyıq. Dördüncü sətirdə xüsusi sinifdən və onun statik metodundan istifadə edirik : ProxynewProxyInstance()
//Создаем прокси нашего an object vasia
Person proxyVasia = (Person) Proxy.newProxyInstance(vasiaClassLoader, interfaces, new PersonInvocationHandler(vasia));
Bu üsul sadəcə bizim proxy obyektimizi yaradır. Metodaya əvvəlki addımda aldığımız orijinal sinif haqqında məlumatları (o ClassLoadervə onun interfeyslərinin siyahısı), həmçinin daha əvvəl yaratdığımız kəsici obyekti - InvocationHandler'a. Əsas odur ki, orijinal obyektimizi kəsiciyə ötürməyi unutmayın vasia, əks halda onun "tutmaq" üçün heç bir şeyi olmayacaq :) Sonumuz nə oldu? İndi bir proxy obyektimiz var vasiaProxy. İstənilən interfeys metodlarınıPerson çağıra bilər . Niyə? Çünki biz ona bütün interfeyslərin siyahısını verdik - burada:
//Получаем все интерфейсы, которые реализует оригинальный an object
Class[] interfaces = vasia.getClass().getInterfaces();

//Создаем прокси нашего an object vasia
Person proxyVasia = (Person) Proxy.newProxyInstance(vasiaClassLoader, interfaces, new PersonInvocationHandler(vasia));
İndi o, bütün interfeys üsullarından "xəbərdardır" Person. PersonInvocationHandlerBundan əlavə, biz proxy-mizə obyektlə işləmək üçün konfiqurasiya edilmiş obyekti ötürdük vasia:
//Создаем прокси нашего an object vasia
Person proxyVasia = (Person) Proxy.newProxyInstance(vasiaClassLoader, interfaces, new PersonInvocationHandler(vasia));
İndi proksi obyektində hər hansı bir interfeys metodunu çağırsaq Person, ələ keçiricimiz bu çağırışı “tutacaq” və əvəzinə öz metodunu yerinə yetirəcək invoke(). Gəlin metodu işlətməyə çalışaq main()! Konsol çıxışı: Salam! Əla! Görürük ki, real metod əvəzinə metodumuz Person.introduce()belə adlanır : invoke()PersonInvocationHandler()
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

   System.out.println("Hello!");
   return null;
}
Və konsolda "Salam!" Amma bu, tam olaraq əldə etmək istədiyimiz davranış deyil :/ Fikrimizə görə, əvvəlcə “Salam!” görünməli, sonra çağırdığımız metodun özü işləməlidir. Başqa sözlə, bu üsul çağırır:
proxyVasia.introduce(vasia.getName());
konsola “Salam! Mənim adım Vasyadır” və sadəcə “Salam!” deyil. Buna necə nail ola bilərik? Mürəkkəb bir şey yoxdur: sadəcə olaraq bizim ələ keçiricimiz və metodumuzla bir az məşğul olmalısınız invoke():) Bu üsula hansı arqumentlərin ötürüldüyünə diqqət yetirin:
public Object invoke(Object proxy, Method method, Object[] args)
Metodun invoke()əvəzinə çağırdığı metoda və onun bütün arqumentlərinə (Metod metodu, Obyekt[] args) çıxışı var. Başqa sözlə desək, metodu çağırsaq proxyVasia.introduce(vasia.getName())və metod əvəzinə introduce()metod çağırılırsa invoke(), həmin metodun daxilində həm orijinal metoda, həm də onun arqumentinə çıxışımız olur introduce()! Nəticədə belə bir şey edə bilərik:
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()İndi biz metoda orijinal metoda zəng əlavə etdik . İndi əvvəlki nümunəmizdəki kodu işlətməyə çalışsaq:
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());
   }
}
onda görəcəyik ki, indi hər şey lazım olduğu kimi işləyir :) Konsol çıxışı: Salam! Mənim adım Vasyadır. Harada lazım ola bilər? Əslində bir çox yerlərdə. Dynamic Proxy“Dinamik proxy” dizayn nümunəsi məşhur texnologiyalarda fəal şəkildə istifadə olunur... və yeri gəlmişkən, bunun bir nümunə olduğunu söyləməyi unutmuşam ! Təbrik edirik, başqa bir şey öyrəndiniz! :) Dinamik proksilər - 2Beləliklə, o, təhlükəsizliklə bağlı məşhur texnologiyalarda və çərçivələrdə fəal şəkildə istifadə olunur. Təsəvvür edin ki, yalnız proqramınızın daxil olan istifadəçiləri tərəfindən icra edilə bilən 20 metodunuz var. Öyrəndiyiniz üsullardan istifadə edərək, hər bir metodda ayrıca yoxlama kodunu təkrarlamadan istifadəçinin giriş və şifrə daxil olub-olmadığını yoxlamaq üçün bu 20 üsula asanlıqla yoxlama əlavə edə bilərsiniz. Və ya, məsələn, bütün istifadəçi hərəkətlərinin qeyd olunacağı bir jurnal yaratmaq istəyirsinizsə, bunu bir proxy istifadə edərək etmək də asandır. Bu, indi də mümkündür: sadəcə nümunəyə kod əlavə edin ki, çağırılanda metodun adı konsolda görünsün invoke()və proqramımızın sadə jurnalını əldə edəcəksiniz :) Mühazirənin sonunda bir vacib məsələyə diqqət yetirin . məhdudiyyət . Proksi obyektin yaradılması sinif səviyyəsində deyil, interfeys səviyyəsində baş verir. İnterfeys üçün proxy yaradılır. Bu koda nəzər salın:
//Создаем прокси нашего an object vasia
Person proxyVasia = (Person) Proxy.newProxyInstance(vasiaClassLoader, interfaces, new PersonInvocationHandler(vasia));
Burada biz interfeys üçün xüsusi olaraq proxy yaradırıq Person. Sinif üçün proksi yaratmağa çalışsaq, yəni linkin tipini dəyişib klassa yayımlamağa çalışsaq Man, heç nə alınmayacaq.
Man proxyVasia = (Man) Proxy.newProxyInstance(vasiaClassLoader, interfaces, new PersonInvocationHandler(vasia));

proxyVasia.introduce(vasia.getName());
"Əsas" mövzuda istisna java.lang.ClassCastException: com.sun.proxy.$Proxy0 Man-a ötürülə bilməz. İnterfeys mövcudluğu məcburi tələbdir. Proksi interfeys səviyyəsində işləyir. Bu günə qədər hamısı budur :) Proksilər mövzusunda əlavə material olaraq sizə əla video və həmçinin yaxşı məqalə tövsiyə edə bilərəm . Yaxşı, indi bir neçə problemi həll etmək yaxşı olardı! :) Görüşənədək!
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION