— Salom, Amigo.
— Zo'r, Risha.
— Bugun men sizga yangi va judayam qiziqarli mavzu haqida gapirib beraman — dinamik proksilar.
Java'da kerakli klassning funksiyalarini o'zgartirishning bir nechta usuli mavjud…
Birinchi usul. Meros olish
Ma'lum bir klassning xatti-harakatini o'zgartirishning eng sodda usuli – bu yangi klass yaratish, uni original (asosiy) klassdan meros olish va uning metodlarini qayta aniqlashdir. Keyin, original klassning ob'ektlari o'rniga, meros olingan klass ob'ektlarini ishlatish kerak. Misol:
Reader reader = new UserCustomReader();
Ikkinchi usul. Wrapper-klassdan foydalanish.
Bunday klass misoli BufferedReader. Avvalambor, u Readerdan meros olgan, ya'ni uning o'rniga foydalanilishi mumkin. Ikkinchidan, u barcha murojaatlarni original Reader ob'ektiga yo'naltiradi, uni BufferedReader'ning konstruktori orqali o'tkazish kerak. Misol:
Reader readerOriginal = new UserCustomReader();
Reader reader = new BufferedReader(readerOriginal);
Uchinchi usul. Dinamik proksi (Proxy) yaratish.
Java'da maxsus klass (java.lang.reflect.Proxy) mavjud, uning yordamida programmani bajarish vaqtida (dinamik tarzda) ob'ekt yaratish mumkin, uni yakka klass yaratmasdan.
Bu juda ham oddiy:
Reader reader = (Reader)Proxy.newProxyInstance();
— Ha, bu yangi bir narsa ekan!
— Lekin, bizga oddiy metodlarsiz ob'ekt kerak emas. Ushbu ob'ektda metodlar bo'lishi kerak va ular bizga kerakli narsalarni bajarishi kerak. Buning uchun Java'da maxsus InvocationHandler interfeysi mavjud, uning yordamida barcha metod chaqiruvlarini tutish mumkin, proksi ob'ektiga murojaat qilingan. Proksi ob'ekt faqat interfeyslardan foydalangan holda yaratish mumkin.
Invoke – bu faqat qandaydir metodni chaqirish uchun mo'ljallangan metod/klass standarti.
Handler – bu qandaydir voqeani qayta ishlaydigan klass uchun standart nom. Masalan, sichqoncha bosish qayta ishlovchilari MouseClickHandler deb ataladi, va hokazo.
Interface InvocationHandler faqat bitta invoke metodiga ega bo'lib, unga barcha proksi ob'ektiga berilgan chaqiriqlar yo'naltiriladi. Misol:
Reader reader = (Reader)Proxy.newProxyInstance(new CustomInvocationHandler());
reader.close();
class CustomInvocationHandler implements InvocationHandler
{
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
System.out.println("yes!");
return null;
}
}
Metod reader.close chaqirilganda, invoke metodi chaqiriladi va ekranda “yes!” xabari chiqariladi.
— Ya'ni, biz CustomInvocationHandler nomli klass yaratdik, unda InvocationHandler interfeysini va uning invoke metodini implementatsiya qildik. Invoke metodi chaqirilganda “yes!” satri ekranga chiqariladi - So'ngra biz CustomInvocationHandler turidagi ob'ekt yaratdik va uni proksi ob'ekt yaratishda newProxyInstance metodiga berdik.
— Ha, hammasi to'g'ri.
Bu juda kuchli vosita, odatda bunday proksi yaratish boshqa kompyuterda fizik tarzda ishga tushirilgan dasturlardan ob'ektlarni taqlid qilish uchun ishlatiladi. Yoki kirishni nazorat qilish uchun
– bunday metodda joriy foydalanuvchi huquqlarini tekshirish, xatolarni qayta ishlash, xatolarni loglash va ko'proq narsalarni amalga oshirish mumkin.
Misol, bu yerda invoke metodi original ob'ektlardan metodlarni chaqiradi:
Reader original = new UserCustomReader(); Reader reader = (Reader)Proxy.newProxyInstance(new CustomInvocationHandler(original));
reader.close();
class CustomInvocationHandler implements InvocationHandler { private Reader readerOriginal; CustomInvocationHandler(Reader readerOriginal)
{
this.readerOriginal = readerOriginal; }
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
if (method.getName().equals("close"))
{
System.out.println("Reader closed!");
}
// bu readerOriginal ob'ekti uchun close metodidagi chaqiruv
// metod nomi va parametrlarining ta'rifi method o'zgaruvchisida saqlanadi
return method.invoke(readerOriginal, args);
}
}
Bu misol ikki o'ziga xos jihatga ega.
Birinchidan, konstruktor orqali “original” Reader ob'ekti uzatiladi, unga havola CustomInvocationHandler ichida saqlanadi.
Ikkinchidan, invoke metodida biz yana shu metodni chaqiramiz, lekin allaqachon “original” ob'ektdan.
— Ha, ya'ni shu oxirgi satr allaqachon original ob'ektdan xuddi o'sha metodni chaqiruv:
return method.invoke(readerOriginal, args);
— Ha.
— Shuncha aniq deb aytmayman, lekin baribir tushunarli. Aftidan.
— Ajoyib. Keyin mana bu ham. newProxyInstance metodiga proksi ob'ektni yaratish uchun yana bir oz xizmat ma'lumotlarini uzatish kerak. Ammo, biz katta proksi ob'ektlar yaratmaganimiz uchun, ushbu ma'lumotlarni original klassdan olish juda oson.
Mana, senga misol:
Reader original = new UserCustomReader();
ClassLoader classLoader = original.getClass().getClassLoader();
Class<?>[] interfaces = original.getClass().getInterfaces();
CustomInvocationHandler invocationHandler = new CustomInvocationHandler(original);
Reader reader = (Reader)Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
class CustomInvocationHandler implements InvocationHandler
{
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
return null;
}
}
— Ha, ClassLoader va interfeyslar ro'yxati. Bu Reflection bilan bog'liq, to'g'rimi?
— Ha.
— Tushunarli. Endi, agar kerak bo'lsa, men oddiy bir proksi ob'ekt yaratishim mumkin deb o'ylayman.
— Diego'dan so'ra.
GO TO FULL VERSION