JavaRush /Java блогу /Random-KY /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ибизге кайсы ыкма чакырыларын билбейбиз, андыктан алардын бирине чалып жатканда ал адам биринчи “Салам!” деп айтышын каалайбыз. (адепсиз адамды эч ким жактырbyte). Динамикалык проксилер - 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(). Ал, чындыгында, бизге керектүү нерсени жасайт - ал биздин an objectибизге бардык методдук чакырууларды кармап, керектүү жүрүм-турумду кошот (бул жерде биз invoke()методдун ичиндеги консолго "Салам!" басып чыгарабыз).
  1. Түпнуска an object жана анын прокси.
Келгиле, оригиналдуу an objectти 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ти жасайбыз. Төмөнкү эки сап башаламандыкка алып келиши мүмкүн:
//Получаем загрузчик класса у оригинального an object
ClassLoader vasiaClassLoader = vasia.getClass().getClassLoader();

//Получаем все интерфейсы, которые реализует оригинальный an object
Class[] interfaces = vasia.getClass().getInterfaces();
Бирок бул жерде чындап эле өзгөчө эч нерсе жок :) Прокси түзүү үчүн бизге ClassLoaderбаштапкы an objectтин (класс жүктөөчүсү) жана баштапкы классыбыз (б.а. Man) ишке ашырган бардык интерфейстердин тизмеси керек. Эгер бул эмне экенин билбесеңиз , класстарды JVMге жүктөө жөнүндө бул макаланы же HabréденClassLoader окуй аласыз , бирок аны менен көп убара болбоңуз. Жөн гана биз прокси an objectисин түзүшүбүз керек болгон бир аз кошумча маалымат алып жатканыбызды унутпаңыз. Төртүнчү сапта биз атайын классты жана анын статикалык ыкмасын колдонобуз : ProxynewProxyInstance()
//Создаем прокси нашего an object vasia
Person proxyVasia = (Person) Proxy.newProxyInstance(vasiaClassLoader, interfaces, new PersonInvocationHandler(vasia));
Бул ыкма жөн гана прокси an objectибизди түзөт. Методго биз мурунку кадамда алган оригиналдуу класс (ал ClassLoaderжана анын интерфейстеринин тизмеси), ошондой эле мурда түзүлгөн интерцептордун an objectиси жөнүндө маалыматты өткөрүп беребиз - InvocationHandler'a. Эң негизгиси, биздин баштапкы an objectибизди интерцепторго өткөрүп берүүнү унутпаңыз vasia, антпесе анын "кесorп турган" эч нерсеси болбойт :) Биз эмне менен бүттүк? Бизде азыр прокси an objectи бар 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 менен иштөө үчүн конфигурацияланган an objectти өткөрдүк vasia:
//Создаем прокси нашего an object vasia
Person proxyVasia = (Person) Proxy.newProxyInstance(vasiaClassLoader, interfaces, new PersonInvocationHandler(vasia));
Эми, эгерде биз прокси an objectисинде кандайдыр бир интерфейс ыкмасын чакырсак 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()анын ордуна чакырылган методго жана анын бардык аргументтерине (Метод ыкмасы, Объект[] аргументтерине) кире алат. Башкача айтканда, методду чакырсак 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Ошентип, ал коопсуздукка байланыштуу популярдуу технологияларда жана алHowтарда жигердүү колдонулат. Сиздин программаңызга кирген колдонуучулар гана аткара турган 20 ыкмаңыз бар деп элестетиңиз. Өзүңүз үйрөнгөн ыкмаларды колдонуу менен, ар бир ыкмада текшерүү codeун өз-өзүнчө кайталабастан, бул 20 ыкмага колдонуучу логин жана паролду киргизген-киргизбегенин текшерүү үчүн текшерүүнү оңой эле кошо аласыз. Же, мисалы, эгер сиз колдонуучунун бардык аракеттери жазыла турган журнал түзгүңүз келсе, прокси аркылуу муну жасоо оңой. Бул азыр деле мүмкүн: жөн гана мисалга codeду кошуңуз, ошондо методдун аталышы консолдо көрсөтүлөт invoke(), ошондо сиз биздин программанын жөнөкөй журналын аласыз :) Лекциянын аягында бир маанилүү нерсеге көңүл буруңуз. чектөө . Прокси an objectин түзүү класс деңгээлинде эмес, интерфейс деңгээлинде болот. Интерфейс үчүн прокси түзүлөт. Бул 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 Man'га чыгаруу мүмкүн эмес. Интерфейстин болушу милдеттүү талап. Прокси интерфейс деңгээлинде иштейт. Бүгүнкү күндө бардыгы ушул :) Прокси темасы боюнча кошумча материал катары мен сизге эң сонун видеону жана ошондой эле жакшы макаланы сунуш кыла алам . Мейли, азыр бир нече маселелерди чечсе жакшы болмок! :) Көрүшкөнчө!
Комментарийлер
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION