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. আমন্ত্রণ হ্যান্ডলার

এটা কি? InvocationHandler হল একটি বিশেষ ইন্টারফেস যা আমাদের অবজেক্টে যেকোন মেথড কল ইন্টারসেপ্ট করতে এবং আমাদের প্রয়োজনীয় অতিরিক্ত আচরণ যোগ করতে দেয়। আমাদের নিজস্ব ইন্টারসেপ্টর তৈরি করতে হবে, অর্থাৎ এই ইন্টারফেসটি প্রয়োগ করে এমন একটি ক্লাস তৈরি করতে হবে। এটি বেশ সহজ:

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;
   }
}
আমাদের শুধুমাত্র একটি ইন্টারফেস পদ্ধতি বাস্তবায়ন করতে হবে: invoke() । এবং, যাইহোক, এটি আমাদের যা প্রয়োজন তা করে: এটি আমাদের অবজেক্টে সমস্ত মেথড কলকে বাধা দেয় এবং প্রয়োজনীয় আচরণ যোগ করে (ইনভোক () পদ্ধতির ভিতরে, আমরা কনসোলে "হাই!" আউটপুট করি)।
  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));
এই পদ্ধতিটি কেবল আমাদের প্রক্সি অবজেক্ট তৈরি করে। আমরা পদ্ধতিতে মূল ক্লাসের তথ্য পাস করি, যা আমরা শেষ ধাপে পেয়েছি (এর ClassLoader এবং এর ইন্টারফেসের একটি তালিকা), সেইসাথে পূর্বে তৈরি InvocationHandler অবজেক্ট। মূল জিনিসটি আমাদের আসল আর্নল্ড অবজেক্টকে আমন্ত্রণ হ্যান্ডলারের কাছে পাস করতে ভুলবেন না , অন্যথায় "হ্যান্ডেল" করার মতো কিছুই থাকবে না :) আমরা কী শেষ করেছি? আমাদের এখন একটি প্রক্সি অবজেক্ট আছে: proxyArnold । এটি ব্যক্তি ইন্টারফেসের যেকোনো পদ্ধতিকে কল করতে পারে। কেন? কারণ আমরা এখানে সমস্ত ইন্টারফেসের একটি তালিকা দিয়েছি:

// 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));
এখন এটি ব্যক্তি ইন্টারফেসের সমস্ত পদ্ধতি সম্পর্কে জানে । উপরন্তু, আমরা আমাদের প্রক্সিতে একটি PersonInvocationHandler অবজেক্ট পাঠিয়েছি যা আর্নল্ড অবজেক্টের সাথে কাজ করার জন্য কনফিগার করা হয়েছে:

// Create a proxy for our arnold object
Person proxyArnold = (Person) Proxy.newProxyInstance(arnoldClassLoader, interfaces, new PersonInvocationHandler(arnold));
এখন যদি আমরা প্রক্সি অবজেক্টে Person ইন্টারফেসের যেকোন মেথড কল করি , আমাদের হ্যান্ডলার কলটি ইন্টারসেপ্ট করে এবং পরিবর্তে তার নিজস্ব invoke() মেথড এক্সিকিউট করে। এর main() পদ্ধতি চালানোর চেষ্টা করা যাক ! কনসোল আউটপুট:

Hi!
চমৎকার! আমরা দেখতে পাই যে মূল Person.introduce() পদ্ধতির পরিবর্তে , আমাদের PersonInvocationHandler() এর invoke() পদ্ধতিকে বলা হয়:

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

   System.out.println("Hi!");
   return null;
}
"ওহে!" কনসোলে প্রদর্শিত হয়, কিন্তু এটি ঠিক সেই আচরণ নয় যা আমরা চেয়েছিলাম :/ আমরা যা অর্জন করার চেষ্টা করছিলাম তা হল প্রথমে প্রদর্শন করা "হাই!" এবং তারপর মূল পদ্ধতি নিজেই কল করুন।অন্য কথায়, পদ্ধতি কল

proxyArnold.introduce(arnold.getName());
"হাই! আমার নাম আর্নল্ড" প্রদর্শন করা উচিত, কেবল "হাই!" কিভাবে আমরা এই অর্জন করতে পারেন? এটি জটিল নয়: আমাদের হ্যান্ডলার এবং invoke() পদ্ধতির সাথে কিছু স্বাধীনতা নিতে হবে :) এই পদ্ধতিতে কী আর্গুমেন্ট পাস করা হয়েছে সেদিকে মনোযোগ দিন:

public Object invoke(Object proxy, Method method, Object[] args)
invoke () মেথডের প্রবেশাধিকার রয়েছে মূলত আমন্ত্রিত পদ্ধতিতে এবং এর সমস্ত আর্গুমেন্টে (পদ্ধতি পদ্ধতি, অবজেক্ট[] আর্গুমেন্ট)। অন্য কথায়, যদি আমরা proxyArnold.introduce(arnold.getName()) মেথড বলি যাতে 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("Hi!");
       return method.invoke(person, args);
   }
}
এখন invoke() পদ্ধতিতে আমরা মূল পদ্ধতিতে একটি কল যুক্ত করেছি। যদি আমরা এখন আমাদের আগের উদাহরণ থেকে কোড চালানোর চেষ্টা করি:

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টি পদ্ধতিতে একটি চেক যোগ করতে পারেন যে প্রতিটি পদ্ধতিতে যাচাইকরণ কোডের নকল না করে ব্যবহারকারী বৈধ শংসাপত্রগুলি প্রবেশ করেছে কিনা। অথবা ধরুন আপনি একটি লগ তৈরি করতে চান যেখানে সমস্ত ব্যবহারকারীর ক্রিয়া রেকর্ড করা হবে। এটি একটি প্রক্সি ব্যবহার করে করাও সহজ। এমনকি এখন, আপনি আমাদের উপরের উদাহরণে কোড যোগ করতে পারেন যাতে আপনি invoke() কল করলে পদ্ধতির নামটি প্রদর্শিত হয় এবং এটি আমাদের প্রোগ্রামের একটি অতি সাধারণ লগ তৈরি করবে :) উপসংহারে, একটি গুরুত্বপূর্ণ সীমাবদ্ধতার দিকে মনোযোগ দিন। একটি প্রক্সি অবজেক্ট ইন্টারফেসের সাথে কাজ করে, ক্লাস নয়। একটি ইন্টারফেসের জন্য একটি প্রক্সি তৈরি করা হয়। এই কোডটি একবার দেখুন:

// 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());
থ্রেড "main" java.lang.ClassCastException-এ ব্যতিক্রম: com.sun.proxy.$Proxy0 ম্যানকে কাস্ট করা যাবে না একটি ইন্টারফেস থাকা একটি পরম প্রয়োজন। প্রক্সি ইন্টারফেসের সাথে কাজ করে। আজকের জন্য এতটুকুই :) আচ্ছা, এখন কয়েকটি কাজ সমাধান করলে ভালো হবে! :) পরের বার পর্যন্ত!
মন্তব্য
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION