CodeGym /Java Blog /Random /Mga Dynamic na Proxies sa Java
John Squirrels
Antas
San Francisco

Mga Dynamic na Proxies sa Java

Nai-publish sa grupo
Hi! Ngayon ay isasaalang-alang 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 susubukan naming 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 "add-on" sa itaas ng orihinal na klase, na nagpapahintulot sa amin na baguhin ang gawi ng orihinal na klase kung kinakailangan. Ano ang ibig sabihin ng "baguhin ang pag-uugali" at paano ito gumagana? Isaalang-alang ang isang simpleng halimbawa. Ipagpalagay na mayroon kaming interface ng Tao at isang simpleng klase ng Tao na nagpapatupad ng interface na ito

public interface Person {

   public void introduce(String name);
  
   public void sayAge(int age);
  
   public void sayWhereFrom(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("My name is " + this.name);
   }

   @Override
   public void sayAge(int age) {
       System.out.println("I am " + this.age + " years old");
   }

   @Override
   public void sayWhereFrom(String city, String country) {

       System.out.println("I'm from " + this.city + ", " + this.country);
   }

   // ...getters, setters, etc.
}
Ang aming klase ng Man ay may 3 paraan: ipakilala, sabihinAge, at sabihinWhereFrom. Isipin na nakuha namin ang klase na ito bilang bahagi ng isang off-the-shelf na JAR library at hindi namin basta-basta maisusulat ang code nito. Ngunit kailangan din nating baguhin ang ugali nito. Halimbawa, hindi namin alam kung aling paraan ang maaaring tawagin sa aming bagay, ngunit gusto naming sabihin ng aming tao ang "Hi!" (walang may gusto sa isang taong walang galang) kapag ang alinman sa mga pamamaraan ay tinatawag. Mga dynamic na proxy - 2Ano ang dapat nating gawin sa ganitong sitwasyon? Kakailanganin namin ang ilang bagay:
  1. InvocationHandler

Ano ito? Ang InvocationHandler ay isang espesyal na interface na nagbibigay-daan sa amin na harangin ang anumang tawag sa pamamaraan sa aming bagay at idagdag ang karagdagang pag-uugali na kailangan namin. Kailangan nating lumikha ng sarili nating interceptor, ibig sabihin, lumikha ng klase na nagpapatupad ng 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("Hi!");
       return null;
   }
}
Kailangan lang nating ipatupad ang isang paraan ng interface: invoke() . At, sa pamamagitan ng paraan, ginagawa nito ang kailangan namin: hinarang nito ang lahat ng mga tawag sa pamamaraan sa aming bagay at idinagdag ang kinakailangang pag-uugali (sa loob ng invoke() na pamamaraan, naglalabas kami ng "Hi!" sa console).
  1. Ang orihinal na bagay at ang mga proxy nito.
Lumilikha kami ng aming orihinal na bagay na Man at isang "add-on" (proxy) para dito:

import java.lang.reflect.Proxy;

public class Main {

   public static void main(String[] args) {

       // Create the original object
       Man arnold = new Man("Arnold", 30, "Thal", "Austria");

       // Get the class loader from the original object
       ClassLoader arnoldClassLoader = arnold.getClass().getClassLoader();

       // Get all the interfaces that the original object implements
       Class[] interfaces = arnold.getClass().getInterfaces();

       // Create a proxy for our arnold object
       Person proxyArnold = (Person) Proxy.newProxyInstance(arnoldClassLoader, interfaces, new PersonInvocationHandler(arnold));

       // Call one of our original object's methods on the proxy object
       proxyArnold.introduce(arnold.getName());

   }
}
Hindi ito mukhang napakasimple! Partikular akong nagdagdag ng komento para sa bawat linya ng code. Tingnan natin nang mabuti kung ano ang nangyayari. Sa unang linya, ginagawa lang namin ang orihinal na bagay kung saan gagawa kami ng mga proxy. Ang sumusunod na dalawang linya ay maaaring magdulot sa iyo ng kahirapan:

 // Get the class loader from the original object
ClassLoader arnoldClassLoader = arnold.getClass().getClassLoader();

// Get all the interfaces that the original object implements
Class[] interfaces = arnold.getClass().getInterfaces();
Sa totoo lang, wala talagang espesyal na nangyayari dito :) Sa ikaapat na linya, ginagamit namin ang espesyal na klase ng Proxy at ang static na newProxyInstance() na pamamaraan nito:

// Create a proxy for our arnold object
Person proxyArnold = (Person) Proxy.newProxyInstance(arnoldClassLoader, interfaces, new PersonInvocationHandler(arnold));
Ginagawa lang ng pamamaraang ito ang aming proxy object. Ipinapasa namin sa pamamaraan ang impormasyon tungkol sa orihinal na klase, na natanggap namin sa huling hakbang (ang ClassLoader nito at isang listahan ng mga interface nito), pati na rin ang dati nang nilikhang object na InvocationHandler . Ang pangunahing bagay ay huwag kalimutang ipasa ang aming orihinal na arnold object sa invocation handler, kung hindi, walang "hawakan" :) Ano ang natapos namin? Mayroon na kaming proxy object: proxyArnold . Maaari itong tumawag sa anumang mga pamamaraan ng interface ng Tao . Bakit? Dahil binigyan namin ito ng isang listahan ng lahat ng mga interface dito:

// Get all the interfaces that the original object implements
Class[] interfaces = arnold.getClass().getInterfaces();

// Create a proxy for our arnold object
Person proxyArnold = (Person) Proxy.newProxyInstance(arnoldClassLoader, interfaces, new PersonInvocationHandler(arnold));
Ngayon alam na nito ang tungkol sa lahat ng mga pamamaraan ng interface ng Tao . Bilang karagdagan, ipinasa namin sa aming proxy ang isang object na PersonInvocationHandler na na-configure upang gumana sa arnold object:

// Create a proxy for our arnold object
Person proxyArnold = (Person) Proxy.newProxyInstance(arnoldClassLoader, interfaces, new PersonInvocationHandler(arnold));
Ngayon kung tatawag tayo ng anumang paraan ng interface ng Tao sa object ng proxy, haharangin ng ating handler ang tawag at ipapatupad ang sarili nitong invoke() method sa halip. Subukan nating patakbuhin ang pangunahing() na pamamaraan! Output ng console:

Hi!
Magaling! Nakikita namin na sa halip na ang orihinal na Person.introduce() na pamamaraan, ang invoke() na paraan ng aming PersonInvocationHandler() ay tinatawag na:

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

   System.out.println("Hi!");
   return null;
}
"Hi!" ay ipinapakita sa console, ngunit hindi ito ang eksaktong pag-uugali na gusto namin :/ Ang sinisikap naming makamit ay ipakita muna ang "Hi!” at pagkatapos ay tawagan ang orihinal na pamamaraan mismo. Sa madaling salita, ang tawag sa pamamaraan

proxyArnold.introduce(arnold.getName());
dapat ipakita ang "Hi! My name is Arnold", hindi lang "Hi!" Paano natin ito makakamit? Hindi ito kumplikado: kailangan lang nating kumuha ng ilang kalayaan sa ating handler at sa invoke() na paraan :) Bigyang-pansin kung anong mga argumento ang ipinasa sa pamamaraang ito:

public Object invoke(Object proxy, Method method, Object[] args)
Ang invoke() method ay may access sa orihinal na invoke na paraan, at sa lahat ng argumento nito (Method method, Object[] args). Sa madaling salita, kung tatawagin natin ang proxyArnold.introduce(arnold.getName()) na paraan upang ang invoke() na pamamaraan ay tawagin sa halip na ang introduce() na paraan, kung gayon sa loob ng paraang ito ay mayroon tayong access sa orihinal na paraan ng introduce() at ang argumento nito! Bilang resulta, magagawa natin ito:

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("Hi!");
       return method.invoke(person, args);
   }
}
Ngayon sa invoke() na pamamaraan ay nagdagdag kami ng isang tawag sa orihinal na 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) {

       // Create the original object
       Man arnold = new Man("Arnold", 30, "Thal", "Austria");

       // Get the class loader from the original object
       ClassLoader arnoldClassLoader = arnold.getClass().getClassLoader();

       // Get all the interfaces that the original object implements
       Class[] interfaces = arnold.getClass().getInterfaces();

       // Create a proxy for our arnold object
       Person proxyArnold = (Person) Proxy.newProxyInstance(arnoldClassLoader, interfaces, new PersonInvocationHandler(arnold));

       // Call one of our original object's methods on the proxy object
       proxyArnold.introduce(arnold.getName());
   }
}
pagkatapos ay makikita natin na ngayon ang lahat ay gumagana ayon sa nararapat :) Console output:

Hi! My name is Arnold
Kailan mo maaaring kailanganin ito? Actually, medyo madalas. Ang pattern ng disenyo ng "dynamic proxy" ay aktibong ginagamit sa mga sikat na teknolohiya... Oo nga pala, nakalimutan kong banggitin na ang Dynamic Proxy ay isang pattern ng disenyo! Binabati kita, natutunan mo pa ang isa! :) Mga dynamic na proxy - 3Halimbawa, ito ay aktibong ginagamit sa mga sikat na teknolohiya at mga framework na nauugnay sa seguridad. Isipin na mayroon kang 20 mga pamamaraan na dapat lamang isagawa ng mga user na naka-sign in sa 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 mga wastong kredensyal nang hindi nadodoble ang verification code sa bawat pamamaraan. O ipagpalagay na gusto mong lumikha ng isang log kung saan ang lahat ng mga aksyon ng gumagamit ay itatala. Madali din itong gawin gamit ang isang proxy. Kahit ngayon, maaari ka lang magdagdag ng code sa aming halimbawa sa itaas upang ang pangalan ng pamamaraan ay maipakita kapag tumawag ka invoke() , at iyon ay magbubunga ng napakasimpleng log ng aming programa :) Sa konklusyon, bigyang-pansin ang isang mahalagang limitasyon. Gumagana ang isang proxy object sa mga interface, hindi sa mga klase. Ang isang proxy ay nilikha para sa isang interface. Tingnan ang code na ito:

// Create a proxy for our arnold object
Person proxyArnold = (Person) Proxy.newProxyInstance(arnoldClassLoader, interfaces, new PersonInvocationHandler(arnold));
Dito kami lumikha ng isang proxy na partikular para sa interface ng Tao . Kung susubukan naming gumawa ng proxy para sa klase, ibig sabihin, baguhin ang uri ng sanggunian at subukang mag-cast sa klase ng Man , hindi ito gagana.

Person proxyArnold = (Person) Proxy.newProxyInstance(arnoldClassLoader, interfaces, new PersonInvocationHandler(arnold));

proxyArnold.introduce(arnold.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 isang ganap na kinakailangan. Gumagana ang mga proxy sa mga interface. Iyon lang para sa araw na ito :) Buweno, ngayon ay mainam na lutasin ang ilang mga gawain! :) Hanggang sa muli!
Mga komento
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION