హాయ్! ఈ రోజు మనం చాలా ముఖ్యమైన మరియు ఆసక్తికరమైన అంశాన్ని పరిశీలిస్తాము: జావాలో డైనమిక్ ప్రాక్సీ తరగతుల సృష్టి. ఇది చాలా సులభం కాదు, కాబట్టి మేము ఉదాహరణలను ఉపయోగించి దాన్ని గుర్తించడానికి ప్రయత్నిస్తాము :) కాబట్టి, అత్యంత ముఖ్యమైన ప్రశ్న: డైనమిక్ ప్రాక్సీలు అంటే ఏమిటి మరియు అవి దేనికి? ప్రాక్సీ క్లాస్ అనేది ఒరిజినల్ క్లాస్ పైన ఉన్న ఒక రకమైన "యాడ్-ఆన్", ఇది అవసరమైతే ఒరిజినల్ క్లాస్ ప్రవర్తనను మార్చడానికి అనుమతిస్తుంది. "ప్రవర్తనను మార్చడం" అంటే ఏమిటి మరియు అది ఎలా పని చేస్తుంది? ఒక సాధారణ ఉదాహరణను పరిగణించండి. మన దగ్గర పర్సన్ ఇంటర్ఫేస్ మరియు ఈ ఇంటర్ఫేస్ని అమలు చేసే సాధారణ మ్యాన్ క్లాస్ ఉన్నాయని అనుకుందాం
ఈ పరిస్థితిలో మనం ఏమి చేయాలి? మాకు కొన్ని విషయాలు అవసరం:
ఉదాహరణకు, ఇది జనాదరణ పొందిన సాంకేతికతలు మరియు భద్రతకు సంబంధించిన ఫ్రేమ్వర్క్లలో చురుకుగా ఉపయోగించబడుతుంది. మీ ప్రోగ్రామ్కు సైన్ ఇన్ చేసిన వినియోగదారులు మాత్రమే అమలు చేయాల్సిన 20 పద్ధతులు మీకు ఉన్నాయని ఊహించండి. మీరు నేర్చుకున్న టెక్నిక్లను ఉపయోగించి, ప్రతి పద్ధతిలో ధృవీకరణ కోడ్ను నకిలీ చేయకుండా వినియోగదారు చెల్లుబాటు అయ్యే ఆధారాలను నమోదు చేశారో లేదో చూడటానికి మీరు ఈ 20 పద్ధతులకు సులభంగా జోడించవచ్చు. లేదా మీరు అన్ని వినియోగదారు చర్యలు రికార్డ్ చేయబడే లాగ్ను సృష్టించాలనుకుంటున్నారని అనుకుందాం. ప్రాక్సీని ఉపయోగించి దీన్ని చేయడం కూడా సులభం. ఇప్పుడు కూడా, మీరు పైన ఉన్న మా ఉదాహరణకి కోడ్ను జోడించవచ్చు, తద్వారా మీరు ఇన్వోక్() అని పిలిచినప్పుడు పద్ధతి పేరు ప్రదర్శించబడుతుంది మరియు అది మా ప్రోగ్రామ్ యొక్క సూపర్ సింపుల్ లాగ్ను ఉత్పత్తి చేస్తుంది :) ముగింపులో, ఒక ముఖ్యమైన పరిమితిపై శ్రద్ధ వహించండి. ప్రాక్సీ ఆబ్జెక్ట్ ఇంటర్ఫేస్లతో పనిచేస్తుంది, తరగతులతో కాదు. ఇంటర్ఫేస్ కోసం ప్రాక్సీ సృష్టించబడుతుంది. ఈ కోడ్ను పరిశీలించండి:
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 లైబ్రరీలో భాగంగా పొందాము మరియు మేము దాని కోడ్ను తిరిగి వ్రాయలేము. అయితే మనం దాని ప్రవర్తనను కూడా మార్చుకోవాలి. ఉదాహరణకు, మన వస్తువుపై ఏ పద్ధతిని పిలవవచ్చో మాకు తెలియదు, కానీ మన వ్యక్తి "హాయ్!" అని చెప్పాలనుకుంటున్నాము. (మర్యాద లేని వ్యక్తిని ఎవరూ ఇష్టపడరు) ఏదైనా పద్ధతులను పిలిచినప్పుడు. 
-
ఇన్వోకేషన్ హ్యాండ్లర్
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() . మరియు, మార్గం ద్వారా, ఇది మనకు అవసరమైనది చేస్తుంది: ఇది మా వస్తువుకు అన్ని పద్ధతి కాల్లను అడ్డుకుంటుంది మరియు అవసరమైన ప్రవర్తనను జోడిస్తుంది (ఇన్వోక్ () పద్ధతిలో, మేము కన్సోల్కు "హాయ్!"ని అవుట్పుట్ చేస్తాము).
- అసలు వస్తువు మరియు దాని ప్రాక్సీలు.
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));
ఈ పద్ధతి కేవలం మా ప్రాక్సీ వస్తువును సృష్టిస్తుంది. మేము చివరి దశలో (దాని క్లాస్లోడర్ మరియు దాని ఇంటర్ఫేస్ల జాబితా), అలాగే గతంలో సృష్టించిన 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));
ఇప్పుడు మనం ప్రాక్సీ ఆబ్జెక్ట్పై పర్సన్ ఇంటర్ఫేస్ యొక్క ఏదైనా పద్ధతిని కాల్ చేస్తే , మా హ్యాండ్లర్ కాల్ని అడ్డగించి, బదులుగా దాని స్వంత ఇన్వోక్() పద్ధతిని అమలు చేస్తుంది. ప్రధాన () పద్ధతిని అమలు చేయడానికి ప్రయత్నిద్దాం ! కన్సోల్ అవుట్పుట్:
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());
"హాయ్! నా పేరు ఆర్నాల్డ్" అని ప్రదర్శించాలి, కేవలం "హాయ్!" మనం దీన్ని ఎలా సాధించగలం? ఇది సంక్లిష్టంగా లేదు: మేము మా హ్యాండ్లర్ మరియు ఇన్వోక్() పద్ధతితో కొంత స్వేచ్ఛను పొందాలి :) ఈ పద్ధతికి ఏ వాదనలు పంపబడ్డాయో శ్రద్ధ వహించండి:
public Object invoke(Object proxy, Method method, Object[] args)
ఇన్వోక్ () పద్ధతికి మొదటగా సూచించబడిన పద్ధతికి మరియు దాని అన్ని ఆర్గ్యుమెంట్లకు (మెథడ్ మెథడ్, ఆబ్జెక్ట్[] ఆర్గ్స్) యాక్సెస్ ఉంది. మరో మాటలో చెప్పాలంటే, మేము 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
మీకు ఇది ఎప్పుడు అవసరం కావచ్చు? నిజానికి, చాలా తరచుగా. "డైనమిక్ ప్రాక్సీ" డిజైన్ నమూనా జనాదరణ పొందిన సాంకేతికతలలో చురుకుగా ఉపయోగించబడుతుంది... ఓహ్, మార్గం ద్వారా, నేను డైనమిక్ ప్రాక్సీ డిజైన్ నమూనా అని చెప్పడం మర్చిపోయాను! అభినందనలు, మీరు మరొకటి నేర్చుకున్నారు! :) 
// 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లో మినహాయింపు ప్రాక్సీలు ఇంటర్ఫేస్లతో పని చేస్తాయి. ఈరోజు కూడా అంతే :) సరే, ఇప్పుడు కొన్ని పనులు పరిష్కరించుకుంటే బాగుంటుంది! :) మరల సారి వరకు!
GO TO FULL VERSION