CodeGym /Java Blog /अनियमित /जावा में गतिशील प्रॉक्सी
John Squirrels
स्तर 41
San Francisco

जावा में गतिशील प्रॉक्सी

अनियमित ग्रुप में प्रकाशित
नमस्ते! आज हम एक महत्वपूर्ण और दिलचस्प विषय पर विचार करेंगे: जावा में डायनेमिक प्रॉक्सी क्लास बनाना। यह बहुत सरल नहीं है, इसलिए हम उदाहरणों का उपयोग करके इसका पता लगाने की कोशिश करेंगे :) तो, सबसे महत्वपूर्ण प्रश्न: डायनेमिक प्रॉक्सी क्या हैं और वे किस लिए हैं? एक प्रॉक्सी वर्ग मूल वर्ग के शीर्ष पर एक प्रकार का "ऐड-ऑन" है, जो हमें आवश्यक होने पर मूल वर्ग के व्यवहार को बदलने की अनुमति देता है। "व्यवहार बदलने" का क्या अर्थ है और यह कैसे काम करता है? एक साधारण उदाहरण पर विचार करें। मान लीजिए कि हमारे पास एक व्यक्ति इंटरफ़ेस है और एक साधारण मैन वर्ग है जो इस इंटरफ़ेस को लागू करता है

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.
}
हमारे मैन क्लास में 3 विधियाँ हैं: परिचय दें, कहें आयु, और कहें जहाँ से। कल्पना कीजिए कि हमें यह क्लास एक ऑफ-द-शेल्फ JAR लाइब्रेरी के हिस्से के रूप में मिली है और हम इसके कोड को फिर से नहीं लिख सकते हैं। लेकिन हमें इसके व्यवहार को भी बदलने की जरूरत है। उदाहरण के लिए, हम नहीं जानते कि हमारे ऑब्जेक्ट पर कौन सी विधि कॉल की जा सकती है, लेकिन हम चाहते हैं कि हमारा व्यक्ति "हाय!" (अशिष्ट व्यक्ति को कोई पसंद नहीं करता) जब किसी भी तरीके को कहा जाता है। डायनेमिक प्रॉक्सी - 2इस स्थिति में हमें क्या करना चाहिए? हमें कुछ चीजों की आवश्यकता होगी:
  1. इनवोकेशनहैंडलर

यह क्या है? इनवोकेशनहैंडलर एक विशेष इंटरफ़ेस है जो हमें किसी भी विधि कॉल को हमारे ऑब्जेक्ट में इंटरसेप्ट करने देता है और हमें आवश्यक अतिरिक्त व्यवहार जोड़ता है। हमें अपना स्वयं का इंटरसेप्टर बनाने की आवश्यकता है, अर्थात एक ऐसा वर्ग बनाएं जो इस इंटरफ़ेस को लागू करे। यह बहुत आसान है:

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;
   }
}
हमें केवल एक इंटरफ़ेस विधि लागू करने की आवश्यकता है: आह्वान () । और, वैसे, यह वही करता है जो हमें चाहिए: यह हमारे ऑब्जेक्ट पर सभी विधि कॉलों को रोकता है और आवश्यक व्यवहार जोड़ता है (आह्वान () विधि के अंदर, हम "हाय!" को कंसोल पर आउटपुट करते हैं)।
  1. मूल वस्तु और उसके प्रतिनिधि।
हम इसके लिए अपना मूल मैन ऑब्जेक्ट और एक "ऐड-ऑन" (प्रॉक्सी) बनाते हैं:

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());

   }
}
यह बहुत आसान नहीं लगता! मैंने विशेष रूप से कोड की प्रत्येक पंक्ति के लिए एक टिप्पणी जोड़ी। आइए देखें कि क्या हो रहा है। पहली पंक्ति में, हम केवल मूल वस्तु बनाते हैं जिसके लिए हम प्रॉक्सी बनाएंगे। निम्नलिखित दो पंक्तियों से आपको कठिनाई हो सकती है:

 // 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();
वास्तव में, यहाँ वास्तव में कुछ खास नहीं हो रहा है :) चौथी पंक्ति में, हम विशेष प्रॉक्सी वर्ग और इसकी स्थिर newProxyInstance () विधि का उपयोग करते हैं:

// Create a proxy for our arnold object
Person proxyArnold = (Person) Proxy.newProxyInstance(arnoldClassLoader, interfaces, new PersonInvocationHandler(arnold));
यह विधि सिर्फ हमारी प्रॉक्सी वस्तु बनाती है। हम विधि को मूल वर्ग के बारे में जानकारी पास करते हैं, जो हमें अंतिम चरण (इसके क्लासलोडर और इसके इंटरफेस की एक सूची) में प्राप्त हुई थी, साथ ही साथ पहले से बनाए गए इनवोकेशनहैंडलर ऑब्जेक्ट को भी। मुख्य बात यह है कि हमारे मूल अर्नोल्ड ऑब्जेक्ट को इनवोकेशन हैंडलर को पास करना न भूलें, अन्यथा "हैंडल" करने के लिए कुछ भी नहीं होगा :) हमने क्या किया? अब हमारे पास प्रॉक्सी ऑब्जेक्ट है: प्रॉक्सीअर्नोल्ड । यह व्यक्ति इंटरफ़ेस के किसी भी तरीके को कॉल कर सकता है। क्यों? क्योंकि हमने इसे यहाँ सभी इंटरफेस की सूची दी है:

// 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));
अब यह व्यक्ति इंटरफेस के सभी तरीकों के बारे में जानता है । इसके अलावा, हमने अपने प्रॉक्सी को अर्नोल्ड ऑब्जेक्ट के साथ काम करने के लिए कॉन्फ़िगर किया गया एक पर्सन इनवोकेशनहैंडलर ऑब्जेक्ट पास किया :

// Create a proxy for our arnold object
Person proxyArnold = (Person) Proxy.newProxyInstance(arnoldClassLoader, interfaces, new PersonInvocationHandler(arnold));
अब अगर हम प्रॉक्सी ऑब्जेक्ट पर पर्सन इंटरफेस की किसी भी विधि को कॉल करते हैं, तो हमारा हैंडलर कॉल को इंटरसेप्ट करता है और इसके बजाय अपने इनवोक () मेथड को निष्पादित करता है। आइए मुख्य () विधि को चलाने का प्रयास करें! कंसोल आउटपुट:

Hi!
उत्कृष्ट! हम देखते हैं कि मूल व्यक्ति.परिचय () विधि के बजाय, हमारे व्यक्ति इनवोकेशनहैंडलर () के आह्वान () विधि को कहा जाता है:

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

   System.out.println("Hi!");
   return null;
}
"नमस्ते!" कंसोल पर प्रदर्शित होता है, लेकिन यह वास्तव में वह व्यवहार नहीं है जो हम चाहते थे :/ हम जो हासिल करने की कोशिश कर रहे थे वह पहले "हाय!" और उसके बाद मूल विधि को ही कॉल करें।दूसरे शब्दों में, विधि कॉल

proxyArnold.introduce(arnold.getName());
"हाय! मेरा नाम अर्नोल्ड है" प्रदर्शित करना चाहिए, न कि केवल "हाय!" हम इसे कैसे प्राप्त कर सकते हैं? यह जटिल नहीं है: हमें बस अपने हैंडलर और इनवोक () विधि के साथ कुछ स्वतंत्रता लेने की आवश्यकता है :) ध्यान दें कि इस विधि में कौन से तर्क दिए गए हैं:

public Object invoke(Object proxy, Method method, Object[] args)
इनवोक () विधि के पास मूल रूप से लागू विधि और इसके सभी तर्कों (विधि विधि, ऑब्जेक्ट [] तर्क) तक पहुंच है। दूसरे शब्दों में, यदि हम प्रॉक्सीअर्नोल्ड.इंट्रोड्यूस(अर्नोल्ड.गेटनेम ()) मेथड को कॉल करते हैं ताकि इंट्रोड्यूस () मेथड के बजाय इनवोक () मेथड को कॉल किया जाए , तो इस मेथड के अंदर हमारे पास मूल इंट्रोड्यूस () मेथड तक पहुंच है। और इसका तर्क! नतीजतन, हम यह कर सकते हैं:

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);
   }
}
अब इनवोक () विधि में हमने मूल विधि में एक कॉल जोड़ा है। यदि हम अब अपने पिछले उदाहरण से कोड चलाने का प्रयास करते हैं:

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());
   }
}
तब हम देखेंगे कि अब सब कुछ वैसा ही काम करता है जैसा उसे करना चाहिए :) कंसोल आउटपुट:

Hi! My name is Arnold
आपको इसकी आवश्यकता कब हो सकती है? दरअसल, अक्सर। "डायनेमिक प्रॉक्सी" डिज़ाइन पैटर्न लोकप्रिय तकनीकों में सक्रिय रूप से उपयोग किया जाता है ... ओह, वैसे, मैं यह उल्लेख करना भूल गया कि डायनेमिक प्रॉक्सी एक डिज़ाइन पैटर्न है! बधाई हो, आपने एक और सीखा! :) डायनेमिक प्रॉक्सी - 3उदाहरण के लिए, यह सुरक्षा से संबंधित लोकप्रिय तकनीकों और रूपरेखाओं में सक्रिय रूप से उपयोग किया जाता है। कल्पना करें कि आपके पास 20 विधियाँ हैं जिन्हें केवल उन उपयोगकर्ताओं द्वारा निष्पादित किया जाना चाहिए जो आपके प्रोग्राम में साइन इन हैं। आपके द्वारा सीखी गई तकनीकों का उपयोग करके, आप आसानी से इन 20 विधियों में यह देखने के लिए एक जांच जोड़ सकते हैं कि क्या उपयोगकर्ता ने प्रत्येक विधि में सत्यापन कोड को दोहराए बिना वैध क्रेडेंशियल्स दर्ज किए हैं। या मान लीजिए आप एक लॉग बनाना चाहते हैं जहां सभी उपयोगकर्ता क्रियाएं रिकॉर्ड की जाएंगी। प्रॉक्सी का उपयोग करना भी आसान है। अब भी, आप बस ऊपर दिए गए हमारे उदाहरण में कोड जोड़ सकते हैं ताकि जब आप इनवोक () कहते हैं तो विधि का नाम प्रदर्शित हो , और यह हमारे कार्यक्रम का एक सुपर सरल लॉग उत्पन्न करेगा :) अंत में, एक महत्वपूर्ण सीमा पर ध्यान दें। एक प्रॉक्सी ऑब्जेक्ट इंटरफेस के साथ काम करता है, क्लास नहीं। इंटरफ़ेस के लिए एक प्रॉक्सी बनाया गया है। इस कोड पर एक नज़र डालें:

// Create a proxy for our arnold object
Person proxyArnold = (Person) Proxy.newProxyInstance(arnoldClassLoader, interfaces, new PersonInvocationHandler(arnold));
यहां हम विशेष रूप से व्यक्ति इंटरफ़ेस के लिए प्रॉक्सी बनाते हैं । यदि हम वर्ग के लिए प्रॉक्सी बनाने का प्रयास करते हैं, यानी संदर्भ के प्रकार को बदलते हैं और मैन क्लास में डालने का प्रयास करते हैं, तो यह काम नहीं करेगा।

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

proxyArnold.introduce(arnold.getName());
"मुख्य" थ्रेड में अपवाद java.lang.ClassCastException: com.sun.proxy.$Proxy0 को मनुष्य के लिए नहीं डाला जा सकता है एक इंटरफ़ेस होना एक परम आवश्यकता है। प्रॉक्सी इंटरफेस के साथ काम करते हैं। आज के लिए बस इतना ही :) अच्छा, अब कुछ कार्यों को हल करना अच्छा होगा! :) अगली बार तक!
टिप्पणियां
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION