JavaRush /Java Blog /Random-TL /Mga Dynamic na Proxies sa Java

Mga Dynamic na Proxies sa Java

Nai-publish sa grupo
Kamusta! Ngayon ay titingnan natin ang isang medyo mahalaga at kawili-wiling paksa - ang paglikha ng mga dynamic na proxy class sa Java. Ito ay hindi masyadong simple, kaya't subukan nating malaman ito gamit ang mga halimbawa :) Kaya, ang pinakamahalagang tanong: ano ang mga dynamic na proxy at para saan ang mga ito? Ang proxy class ay isang uri ng "superstructure" sa orihinal na klase, na nagpapahintulot sa amin na baguhin ang pag-uugali nito kung kinakailangan. Ano ang ibig sabihin ng pagbabago ng pag-uugali at paano ito gumagana? Tingnan natin ang isang simpleng halimbawa. Sabihin nating mayroon kaming isang interface Personat isang simpleng klase Manna nagpapatupad ng interface na ito
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);
   }

   //..геттеры, сеттеры, и т.д.
}
Ang aming klase Manay may 3 paraan: ipakilala ang iyong sarili, sabihin ang iyong edad, at sabihin kung saan ka nanggaling. Isipin natin na natanggap natin ang klase na ito bilang bahagi ng isang nakahanda nang JAR library at hindi basta-basta maaaring kunin at isulat muli ang code nito. Gayunpaman, kailangan nating baguhin ang kanyang pag-uugali. Halimbawa, hindi namin alam kung aling paraan ang tatawagin sa aming object, at samakatuwid gusto naming ang tao ay unang magsabi ng "Hello!" kapag tumatawag sa alinman sa kanila. (walang may gusto sa taong walang galang). Mga dynamic na proxy - 1Ano ang dapat nating gawin sa ganitong sitwasyon? Kakailanganin natin ang ilang bagay:
  1. InvocationHandler

Ano ito? Ito ay maaaring literal na isalin bilang "call interceptor". Inilalarawan nito ang layunin nito nang tumpak. InvocationHandleray isang espesyal na interface na nagpapahintulot sa amin na maharang ang anumang mga tawag sa pamamaraan sa aming bagay at magdagdag ng karagdagang pag-uugali na kailangan namin. Kailangan nating gumawa ng sarili nating interceptor - iyon ay, lumikha ng isang klase at ipatupad ang interface na ito. Ito ay medyo simple:
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;
   }
}
Kailangan nating ipatupad lamang ang isang paraan ng interface - invoke(). Sa katunayan, ginagawa nito ang kailangan namin - hinarang nito ang lahat ng mga tawag sa pamamaraan sa aming bagay at idinagdag ang kinakailangang pag-uugali (dito namin invoke()i-print ang "Hello!" sa console sa loob ng pamamaraan).
  1. Ang orihinal na bagay at ang proxy nito.
Gumawa tayo ng orihinal na bagay Manat isang "superstructure" (proxy) para dito:
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());

   }
}
Mukhang hindi masyadong simple! Partikular akong nagsulat ng komento para sa bawat linya ng code: tingnan natin kung ano ang nangyayari doon.

Sa unang linya, gagawin lang namin ang orihinal na bagay kung saan lilikha kami ng isang proxy. Ang sumusunod na dalawang linya ay maaaring magdulot sa iyo ng pagkalito:
//Получаем загрузчик класса у оригинального an object
ClassLoader vasiaClassLoader = vasia.getClass().getClassLoader();

//Получаем все интерфейсы, которые реализует оригинальный an object
Class[] interfaces = vasia.getClass().getInterfaces();
Ngunit talagang walang espesyal na nangyayari dito :) Upang lumikha ng isang proxy, kailangan namin ClassLoaderang (class loader) ng orihinal na bagay at isang listahan ng lahat ng mga interface na Manipinapatupad ng aming orihinal na klase (i.e.). Kung hindi mo alam kung ano ito ClassLoader, maaari mong basahin ang artikulong ito tungkol sa paglo-load ng mga klase sa JVM o ito sa Habré , ngunit huwag mo na itong masyadong abalahin pa. Tandaan lamang na nakakakuha kami ng kaunting karagdagang impormasyon na kakailanganin naming gawin ang proxy object. Sa ika-apat na linya gumagamit kami ng isang espesyal na klase Proxyat ang static na pamamaraan nito newProxyInstance():
//Создаем прокси нашего an object vasia
Person proxyVasia = (Person) Proxy.newProxyInstance(vasiaClassLoader, interfaces, new PersonInvocationHandler(vasia));
Ginagawa lang ng pamamaraang ito ang aming proxy object. Sa pamamaraan na ipinapasa namin ang impormasyon tungkol sa orihinal na klase na natanggap namin sa nakaraang hakbang (ito ClassLoaderat ang listahan ng mga interface nito), pati na rin ang object ng interceptor na nilikha namin kanina - InvocationHandler'a. Ang pangunahing bagay ay huwag kalimutang ipasa ang aming orihinal na bagay sa interceptor vasia, kung hindi man ay wala itong "i-intercept" :) Ano ang natapos namin? Mayroon na kaming proxy object vasiaProxy. Maaari itong tumawag sa anumang paraan ng interfacePerson . Bakit? Dahil ipinasa namin ito sa isang listahan ng lahat ng mga interface - dito:
//Получаем все интерфейсы, которые реализует оригинальный an object
Class[] interfaces = vasia.getClass().getInterfaces();

//Создаем прокси нашего an object vasia
Person proxyVasia = (Person) Proxy.newProxyInstance(vasiaClassLoader, interfaces, new PersonInvocationHandler(vasia));
Ngayon siya ay "alam" sa lahat ng mga pamamaraan ng interface Person. Bilang karagdagan, ipinasa namin ang aming proxy ng isang bagay PersonInvocationHandlerna na-configure upang gumana sa bagay vasia:
//Создаем прокси нашего an object vasia
Person proxyVasia = (Person) Proxy.newProxyInstance(vasiaClassLoader, interfaces, new PersonInvocationHandler(vasia));
Ngayon, kung tatawagin natin ang anumang paraan ng interface sa object ng proxy Person, "huhuli" ng ating interceptor ang tawag na ito at sa halip ay isasagawa ang sarili nitong paraan invoke(). Subukan nating patakbuhin ang pamamaraan main()! Output ng console: Hello! Malaki! Nakikita namin na sa halip na ang tunay na pamamaraan, ang aming Person.introduce()pamamaraan ay tinatawag na : invoke()PersonInvocationHandler()
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

   System.out.println("Hello!");
   return null;
}
At ang console ay nagpakita ng "Hello!" Ngunit hindi ito eksakto ang pag-uugali na gusto naming makuha :/ Ayon sa aming ideya, dapat munang ipakita ang “Hello!”, at pagkatapos ay dapat gumana ang mismong pamamaraan na tinatawagan namin. Sa madaling salita, ang pamamaraang ito ay tinatawag na:
proxyVasia.introduce(vasia.getName());
dapat mag-output sa console na "Hello! Ang pangalan ko ay Vasya," at hindi lamang "Hello!" Paano natin ito makakamit? Walang kumplikado: kailangan mo lang mag-isip nang kaunti sa aming interceptor at pamamaraan invoke():) Bigyang-pansin kung anong mga argumento ang ipinasa sa pamamaraang ito:
public Object invoke(Object proxy, Method method, Object[] args)
Ang isang pamamaraan invoke()ay may access sa paraan na ito ay tinatawag sa halip at lahat ng mga argumento nito (Paraan ng pamamaraan, Object[] args). Sa madaling salita, kung tatawagin natin ang isang pamamaraan proxyVasia.introduce(vasia.getName()), at sa halip na isang pamamaraan , introduce()ang isang pamamaraan ay tinatawag na invoke(), sa loob ng pamamaraang iyon ay mayroon tayong access sa parehong orihinal na pamamaraan introduce()at ang argumento nito! Bilang isang resulta, maaari naming gawin ang isang bagay tulad nito:
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);
   }
}
Ngayon ay nagdagdag kami invoke()ng isang tawag sa orihinal na pamamaraan sa pamamaraan. Kung susubukan naming patakbuhin ang code mula sa aming nakaraang halimbawa:
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());
   }
}
pagkatapos ay makikita natin na ngayon ang lahat ay gumagana ayon sa nararapat :) Console output: Hello! Ang pangalan ko ay Vasya. Saan mo maaaring kailanganin ito? Sa katunayan, maraming lugar. Ang pattern ng disenyo ng "dynamic proxy" ay aktibong ginagamit sa mga sikat na teknolohiya... at nga pala, nakalimutan kong sabihin sa iyo na Dynamic Proxyito ay isang pattern ! Binabati kita, natutunan mo ang isa pa! :) Mga dynamic na proxy - 2Kaya, ito ay aktibong ginagamit sa mga sikat na teknolohiya at mga framework na nauugnay sa seguridad. Isipin na mayroon kang 20 mga pamamaraan na maaari lamang isagawa ng mga naka-log-in na user ng iyong programa. Gamit ang mga diskarteng natutunan mo, madali mong maidaragdag sa 20 pamamaraang ito ang isang pagsusuri upang makita kung ang user ay nagpasok ng login at password, nang hindi kino-duplicate ang verification code nang hiwalay sa bawat paraan. O, halimbawa, kung gusto mong gumawa ng log kung saan ire-record ang lahat ng pagkilos ng user, madali rin itong gawin gamit ang isang proxy. Posible kahit ngayon: magdagdag lamang ng code sa halimbawa upang ang pangalan ng pamamaraan ay ipinapakita sa console kapag tinawag invoke(), at makakakuha ka ng isang simpleng log ng aming programa :) Sa pagtatapos ng lecture, bigyang-pansin ang isang mahalagang limitasyon . Ang paglikha ng isang proxy object ay nangyayari sa antas ng interface, hindi sa antas ng klase. Ang isang proxy ay nilikha para sa interface. Tingnan ang code na ito:
//Создаем прокси нашего an object vasia
Person proxyVasia = (Person) Proxy.newProxyInstance(vasiaClassLoader, interfaces, new PersonInvocationHandler(vasia));
Dito kami lumikha ng isang proxy na partikular para sa interface Person. Kung susubukan naming gumawa ng proxy para sa klase, ibig sabihin, babaguhin namin ang uri ng link at susubukan naming i-cast sa klase Man, walang gagana.
Man proxyVasia = (Man) Proxy.newProxyInstance(vasiaClassLoader, interfaces, new PersonInvocationHandler(vasia));

proxyVasia.introduce(vasia.getName());
Exception sa thread na "main" java.lang.ClassCastException: com.sun.proxy.$Proxy0 ay hindi maaaring i-cast sa Man Ang pagkakaroon ng interface ay kinakailangan. Gumagana ang proxy sa antas ng interface. Iyon lang para sa araw na ito :) Bilang karagdagang materyal sa paksa ng mga proxy, maaari kong irekomenda sa iyo ang isang mahusay na video at isa ring magandang artikulo . Well, ngayon ay magiging maganda upang malutas ang ilang mga problema! :) See you!
Mga komento
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION