JavaRush /جاوا بلاگ /Random-UR /جاوا میں متحرک پراکسی

جاوا میں متحرک پراکسی

گروپ میں شائع ہوا۔
ہیلو! آج ہم ایک اہم اور دلچسپ موضوع پر غور کریں گے - جاوا میں متحرک پراکسی کلاسز بنانا۔ یہ بہت آسان نہیں ہے، تو آئیے مثالوں سے اسے سمجھنے کی کوشش کریں :) تو، سب سے اہم سوال: متحرک پراکسیز کیا ہیں اور وہ کس لیے ہیں؟ ایک پراکسی کلاس اصل کلاس پر ایک قسم کا "سپر اسٹرکچر" ہے، جو ہمیں ضرورت پڑنے پر اس کے رویے کو تبدیل کرنے کی اجازت دیتا ہے۔ رویے کو تبدیل کرنے کا کیا مطلب ہے اور یہ کیسے کام کرتا ہے؟ آئیے ایک سادہ سی مثال دیکھتے ہیں۔ ہم کہتے ہیں کہ ہمارے پاس ایک انٹرفیس 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 لائبریری کے حصے کے طور پر حاصل کی ہے اور ہم اس کا کوڈ لے کر دوبارہ لکھ نہیں سکتے۔ تاہم، ہمیں اس کے رویے کو تبدیل کرنے کی ضرورت ہے. مثال کے طور پر، ہم نہیں جانتے کہ ہمارے اعتراض پر کس طریقہ کو پکارا جائے گا، اور اس لیے ہم چاہتے ہیں کہ ان میں سے کسی کو کال کرتے وقت وہ شخص پہلے "ہیلو!" کہے۔ (کوئی بھی ایسے شخص کو پسند نہیں کرتا جو بدتمیز ہو)۔ Динамические прокси - 1ایسی حالت میں ہمیں کیا کرنا چاہیے؟ ہمیں چند چیزوں کی ضرورت ہوگی:
  1. InvocationHandler

یہ کیا ہے؟ اس کا لفظی ترجمہ "کال انٹرسیپٹر" کے طور پر کیا جا سکتا ہے۔ یہ اس کے مقصد کو بالکل درست طریقے سے بیان کرتا ہے۔ 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("Hello!");
       return null;
   }
}
ہمیں صرف ایک انٹرفیس طریقہ کو لاگو کرنے کی ضرورت ہے invoke()۔ یہ، درحقیقت، وہی کرتا ہے جس کی ہمیں ضرورت ہوتی ہے - یہ ہمارے آبجیکٹ میں تمام میتھڈ کالز کو روکتا ہے اور ضروری رویے کا اضافہ کرتا ہے (یہاں ہم invoke()طریقہ کے اندر کنسول پر "Hello!" پرنٹ کرتے ہیں)۔
  1. اصل آبجیکٹ اور اس کی پراکسی۔
آئیے 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());

   }
}
بہت سادہ نہیں لگتا! میں نے خاص طور پر کوڈ کی ہر سطر کے لیے ایک تبصرہ لکھا: آئیے قریب سے دیکھیں کہ وہاں کیا ہو رہا ہے۔

پہلی لائن میں ہم صرف اصل آبجیکٹ بناتے ہیں جس کے لیے ہم ایک پراکسی بنائیں گے۔ درج ذیل دو سطریں آپ کو الجھن کا باعث بن سکتی ہیں۔
//Получаем загрузчик класса у оригинального an object
ClassLoader vasiaClassLoader = vasia.getClass().getClassLoader();

//Получаем все интерфейсы, которые реализует оригинальный an object
Class[] interfaces = vasia.getClass().getInterfaces();
لیکن یہاں واقعی کچھ خاص نہیں ہو رہا ہے :) ایک پراکسی بنانے کے لیے، ہمیں ClassLoaderاصل آبجیکٹ کے (کلاس لوڈر) اور ان تمام انٹرفیس کی فہرست کی ضرورت ہے جو ہماری اصل کلاس (یعنی Man) لاگو کرتی ہے۔ اگر آپ نہیں جانتے کہ یہ کیا ہے ClassLoader، تو آپ JVM میں کلاسز لوڈ کرنے کے بارے میں یا Habré پر اس مضمون کو پڑھ سکتے ہیں ، لیکن ابھی تک اس سے زیادہ پریشان نہ ہوں۔ بس یاد رکھیں کہ ہمیں تھوڑی اضافی معلومات مل رہی ہیں جس کے بعد ہمیں پراکسی آبجیکٹ بنانے کی ضرورت ہوگی۔ چوتھی لائن پر ہم ایک خاص کلاس اور اس کا جامد طریقہ استعمال کرتے ہیں : ProxynewProxyInstance()
//Создаем прокси нашего an object vasia
Person proxyVasia = (Person) Proxy.newProxyInstance(vasiaClassLoader, interfaces, new PersonInvocationHandler(vasia));
Этот метод How раз создает наш прокси-an object. В метод мы передаем ту информацию об оригинальном классе, которую получor на прошлом шаге (его ClassLoader и список его интерфейсов), а также an object созданного нами ранее перехватчика — InvocationHandler’a. Главное — не забудь передать перехватчику наш оригинальный an object vasia, иначе ему нечего будет «перехватывать» :) What же у нас в итоге получилось? У нас теперь есть прокси-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. Кроме того, мы передали нашему прокси an object PersonInvocationHandler, настроенный на работу с an objectом vasia:
//Создаем прокси нашего an object vasia
Person proxyVasia = (Person) Proxy.newProxyInstance(vasiaClassLoader, interfaces, new PersonInvocationHandler(vasia));
Теперь, если мы вызовем у прокси-an object любой метод интерфейса Person, наш перехватчик «словит» этот вызов и выполнит instead of него свой метод invoke(). Давай попробуем запустить метод main()! Вывод в консоль: Hello! Отлично! Мы видим, что instead of настоящего метода Person.introduce() вызван метод invoke() нашего PersonInvocationHandler():
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

   System.out.println("Hello!");
   return null;
}
И в консоль было выведено «Hello!» Но это не совсем то поведение, которое мы хотели получить :/ По нашей задумке сначала должно быть выведено «Hello!», а после — сработать сам метод, который мы вызываем. Иными словами, вот этот вызов метода:
proxyVasia.introduce(vasia.getName());
должен выводить в консоль «Hello! Меня зовут Вася», а не просто «Hello!» Как же нам добиться этого? Ничего сложного: просто придется немного похимичить над нашим перехватчиком и методом invoke() :) Обрати внимание, Howие аргументы передаются в этот метод:
public Object invoke(Object proxy, Method method, Object[] args)
У метода invoke() есть доступ к методу, instead of которого он вызывается, и ко всем его аргументам (Method method, Object[] args). Иными словами, если мы вызываем метод proxyVasia.introduce(vasia.getName()), и instead of метода 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);
   }
}
Теперь мы добавor в метод 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());
   }
}
то увидим, что теперь все работает How надо :) Вывод в консоль: Hello! Меня зовут Вася Где это может тебе понадобиться? На самом деле, много где. Паттерн проектирования «динамический прокси» активно используется в популярных технологиях...а я, кстати, и забыл тебе сказать, что Dynamic Proxy — это паттерн! Поздравляю, ты выучил еще один! :) Динамические прокси - 2Так вот, он активно используется в популярных технологиях и фреймворках, связанных с безопасностью. Представь, что у тебя есть 20 методов, которые могут выполнять только залогиненные пользователи твоей программы. С помощью изученных приемов ты легко сможешь добавить в эти 20 методов проверку того, ввел ли пользователь логин и пароль, не дублируя code проверки отдельно в каждом методе. Или, к примеру, если ты хочешь создать журнал, куда будут записываться все действия пользователей, это также легко сделать с использованием прокси. Можно даже сейчас: просто допиши в пример code, чтобы название метода выводилось в консоль при вызове invoke(), и ты получишь простенький журнал логов нашей программы :) В завершение лекции, обрати внимание на одно важное ограничение. Creation прокси 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());
Exception in thread "main" java.lang.ClassCastException: com.sun.proxy.$Proxy0 cannot be cast to Man Наличие интерфейса — обязательное требование. Прокси работает на уровне интерфейсов. На этом на сегодня все :) В качестве дополнительного материала по теме прокси могу порекомендовать тебе отличное видео, и также неплохую статью. Ну, а теперь было бы неплохо решить несколько задач! :) До встречи!
تبصرے
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION