JavaRush /Java блогы /Random-KK /Java тіліндегі динамикалық проксилер

Java тіліндегі динамикалық проксилер

Топта жарияланған
Сәлеметсіз бе! Бүгін біз өте маңызды және қызықты тақырыпты қарастырамыз - Java-да динамикалық прокси сыныптарын құру. Бұл өте қарапайым емес, сондықтан оны мысалдармен анықтауға тырысайық :) Сонымен, ең маңызды сұрақ: динамикалық прокси дегеніміз не және олар не үшін қажет? Прокси класс - бұл қажет болған жағдайда оның әрекетін өзгертуге мүмкіндік беретін бастапқы сыныпқа қатысты «қондырманың» бір түрі. Мінез-құлықты өзгерту нені білдіреді және ол қалай жұмыс істейді? Қарапайым мысалды қарастырайық. Бізде интерфейс және осы интерфейсті жүзеге асыратын Personқарапайым класс бар делікMan
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);
   }

   //..геттеры, сеттеры, и т.д.
}
Біздің сыныпта Man3 әдіс бар: өзіңді таныстыру, жасыңды айту және қай жерден екеніңді айту. Біз бұл классты дайын JAR кітапханасының бөлігі ретінде алдық және оның codeын жай ғана алып, қайта жаза алмаймыз деп елестетіп көрейік. Дегенмен, оның мінез-құлқын өзгертуіміз керек. Мысалы, біз an objectімізде қандай әдіс шақырылатынын білмейміз, сондықтан біз олардың кез келгеніне қоңырау шалған кезде адамның алдымен «Сәлеметсіз бе!» Дегенін қалаймыз. (әдепсіз адамды ешкім ұнатпайды). Динамикалық прокси - 1Мұндай жағдайда не істеуіміз керек? Бізге бірнеше нәрсе қажет болады:
  1. InvocationHandler

Бұл не? Оны сөзбе-сөз аударуға болады «қоңырауды тоқтату». Бұл оның мақсатын өте дәл сипаттайды. InvocationHandlerбұл біздің an objectімізге кез келген әдісті шақыруға және бізге қажет қосымша әрекетті қосуға мүмкіндік беретін арнайы интерфейс. Біз өзіміздің интерцепторды жасауымыз керек - яғни класс құру және осы интерфейсті жүзеге асыру. Бұл өте қарапайым:
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;
   }
}
Бізге бір ғана интерфейс әдісін енгізу керек - invoke(). Ол, шын мәнінде, бізге қажет нәрсені жасайды - ол біздің нысанға барлық әдісті шақырады және қажетті әрекетті қосады (мұнда invoke()әдіс ішіндегі консольге «Сәлеметсіз бе!» деп басып шығарамыз).
  1. Түпнұсқа нысан және оның прокси.
ManТүпнұсқа нысанды және ол үшін «қондырма» (прокси) жасайық :
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());

   }
}
Өте қарапайым көрінбейді! Мен codeтың әрбір жолына арнайы түсініктеме жаздым: онда не болып жатқанын толығырақ қарастырайық.

Бірінші жолда біз прокси жасайтын түпнұсқа нысанды жасаймыз. Келесі екі жол сізді шатастыруы мүмкін:
//Получаем загрузчик класса у оригинального an object
ClassLoader vasiaClassLoader = vasia.getClass().getClassLoader();

//Получаем все интерфейсы, которые реализует оригинальный an object
Class[] interfaces = vasia.getClass().getInterfaces();
Бірақ мұнда шынымен де ерекше ештеңе жоқ :) Прокси жасау үшін бізге ClassLoaderбастапқы нысанның (сынып жүктеушісі) және бастапқы класс (яғни Man) жүзеге асыратын барлық интерфейстердің тізімі қажет. Егер сіз оның не екенін білмесеңіз , сіз JVM-ге сыныптарды жүктеу туралы осы мақаланы немесе Habré-де осыClassLoader мақаланы оқи аласыз , бірақ онымен тым көп алаңдамаңыз. Есіңізде болсын, біз прокси нысанын жасау үшін қажет болатын қосымша ақпарат аламыз. Төртінші жолда біз арнайы классты және оның статикалық әдісін қолданамыз : ProxynewProxyInstance()
//Создаем прокси нашего an object vasia
Person proxyVasia = (Person) Proxy.newProxyInstance(vasiaClassLoader, interfaces, new PersonInvocationHandler(vasia));
Бұл әдіс прокси нысанымызды жасайды. Әдіске біз алдыңғы қадамда алған бастапқы класс туралы ақпаратты (ол ClassLoaderжәне оның интерфейстерінің тізімі), сондай-ақ бұрын жасаған интерцептордың an objectісін береміз - InvocationHandler'a. Ең бастысы, біздің бастапқы нысанымызды интерцепторға беруді ұмытпаңыз vasia, әйтпесе оның «кесінді» ештеңесі болмайды :) Біз немен аяқталдық? Бізде қазір прокси нысаны бар vasiaProxy. Ол кез келген интерфейс әдістерінPerson шақыра алады . Неліктен? Өйткені біз оған барлық интерфейстердің тізімін бердік - мұнда:
//Получаем все интерфейсы, которые реализует оригинальный an object
Class[] interfaces = vasia.getClass().getInterfaces();

//Создаем прокси нашего an object vasia
Person proxyVasia = (Person) Proxy.newProxyInstance(vasiaClassLoader, interfaces, new PersonInvocationHandler(vasia));
Енді ол интерфейстің барлық әдістерінен «хабардар» Person. Сонымен қатар, біз проксиге PersonInvocationHandleran objectімен жұмыс істеу үшін конфигурацияланған нысанды жібердік vasia:
//Создаем прокси нашего an object vasia
Person proxyVasia = (Person) Proxy.newProxyInstance(vasiaClassLoader, interfaces, new PersonInvocationHandler(vasia));
Енді прокси нысанында кез келген интерфейс әдісін шақыратын болсақ Person, біздің интерцептор бұл шақыруды «ұстап алады» және оның орнына өз әдісін орындайды invoke(). Әдісті іске қосып көрейік main()! Консоль шығысы: Сәлем! Тамаша! Біз нақты әдістің орнына біздің Person.introduce()әдіс деп аталатынын көреміз : invoke()PersonInvocationHandler()
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

   System.out.println("Hello!");
   return null;
}
Ал консольде «Сәлеметсіз бе! Бірақ бұл дәл біз алғымыз келген мінез-құлық емес:/ Біздің ойымыз бойынша, алдымен «Сәлеметсіз бе!» көрсетілуі керек, содан кейін біз шақыратын әдістің өзі жұмыс істеуі керек. Басқаша айтқанда, бұл әдіс шақырады:
proxyVasia.introduce(vasia.getName());
консольге «Сәлеметсіз бе! Менің атым Вася, жай ғана «Сәлем!» емес! Бұған қалай қол жеткізе аламыз? Ешқандай күрделі ештеңе жоқ: сіз біздің интерцепторымызбен және әдісімізбен аздап ойлануыңыз керек invoke():) Бұл әдіске қандай дәлелдер берілгеніне назар аударыңыз:
public Object invoke(Object proxy, Method method, Object[] args)
Әдіс invoke()оның орнына шақырылатын әдіске және оның барлық аргументтеріне (Method әдісі, Object[] args) рұқсаты бар. Басқаша айтқанда, әдісті шақырсақ proxyVasia.introduce(vasia.getName())және әдістің орнына introduce()әдіс деп аталады invoke(), бұл әдіс ішінде біз бастапқы әдіске де introduce(), оның аргументіне де қол жеткізе аламыз! Нәтижесінде біз келесідей нәрсені жасай аламыз:
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()Енді біз әдіске бастапқы әдіске шақыруды қостық . Енді біз алдыңғы мысалдағы codeты іске қосуға тырыссақ:
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());
   }
}
содан кейін біз қазір бәрі дұрыс жұмыс істейтінін көреміз :) Консоль шығысы: Сәлеметсіз бе! Менің атым Вася, ол сізге қай жерде қажет болуы мүмкін? Шын мәнінде, көптеген жерлер. «Динамикалық прокси» дизайн үлгісі танымал технологияларда белсенді түрде қолданылады... және айтпақшы, Dynamic Proxyбұл үлгі екенін айтуды ұмытып кеттім ! Құттықтаймыз, сіз тағы бір нәрсені үйрендіңіз! :) Динамикалық прокси - 2Сонымен, ол қауіпсіздікке қатысты танымал технологиялар мен фреймворктарда белсенді қолданылады. Сізде бағдарламаның жүйеге кірген пайдаланушылары ғана орындай алатын 20 әдіс бар деп елестетіп көріңіз. Үйренген әдістерді пайдалана отырып, әрбір әдісте растау codeын бөлек қайталамай-ақ, пайдаланушының логин мен құпия сөзді енгізгенін тексеру үшін осы 20 әдіске оңай қосуға болады. Немесе, мысалы, барлық пайдаланушы әрекеттері жазылатын журналды жасағыңыз келсе, мұны прокси арқылы орындау да оңай. Сіз тіпті қазір де істей аласыз: мысалға codeты қосыңыз, осылайша әдіс атауы шақырылған кезде консольде көрсетіледі invoke()және сіз біздің бағдарламаның қарапайым журналын аласыз :) Дәріс соңында бір маңыздыға назар аударыңыз . шектеу . Прокси нысанын жасау сынып деңгейінде емес, интерфейс деңгейінде орын алады. Интерфейс үшін прокси жасалады. Мына codeты қараңыз:
//Создаем прокси нашего an object vasia
Person proxyVasia = (Person) Proxy.newProxyInstance(vasiaClassLoader, interfaces, new PersonInvocationHandler(vasia));
Мұнда интерфейс үшін арнайы прокси жасаймыз Person. Егер біз сынып үшін прокси құруға тырыссақ, яғни сілтеме түрін өзгертіп, сыныпқа трансляциялауға тырыссақ Man, ештеңе жұмыс істемейді.
Man proxyVasia = (Man) Proxy.newProxyInstance(vasiaClassLoader, interfaces, new PersonInvocationHandler(vasia));

proxyVasia.introduce(vasia.getName());
"Негізгі" java.lang.ClassCastException: com.sun.proxy.$Proxy0 ағынындағы ерекше жағдай. Адамға трансляциялау мүмкін емес Интерфейстің болуы міндетті талап. Прокси интерфейс деңгейінде жұмыс істейді. Бүгінге дейін бәрі :) Прокси-serverлер тақырыбына қосымша материал ретінде мен сізге тамаша бейнені және жақсы мақаланы ұсына аламын . Енді бірнеше мәселені шешіп алсаңыз жақсы болар еді! :) Кездескенше!
Пікірлер
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION