The proxy pattern is a structural design pattern.
In the proxy pattern, a proxy class is used to control access to another class.
The reasons for this control can vary. As one example, a proxy may avoid instantiation of an object until the object is needed.
This can be useful if the object requires a lot of time or resources to create. Another reason to use a proxy is to control access rights to an object.
A client request may require certain credentials in order to access the object.
Another reason is to log the calls to the original method.
It is also used by mocking frameworks for unit testing.
Using Java Reflection you create dynamic implementations of interfaces at runtime. You do so using the class java.lang.reflect.Proxy.
One example of proxies - Office internet.
We generally use proxies to access the internet in office & these proxies work as a filter/access to the sites being visited.
By these proxies also, admin can have the record of sites being visited by you.
Another example - Proxies during the class/lectures
We all have gone through this phase, where we used to work as a proxy for our friends during the lectures in college.
Means for attendance, that person is not required, so other person is working as a proxy for that missing person.
You create proxies using the Proxy.newProxyInstance() method. The newProxyInstance() methods takes 3 parameters:
The ClassLoader that is to "load" the dynamic proxy class.
An array of interfaces to implement.
An InvocationHandler to forward all methods calls on the proxy to.
Example Code : Note - We need interface for which we can have the proxies, on classes directly it doesn't work.
public interface Interface {
public double findSqrRoot(int num);
}
Original class is calculating the square root of the number provided to its method findSqrRoot(). Lets assume its a heavy method.
public class Original implements Interface {
public double findSqrRoot(int num) {
return Math.sqrt(num);
}
}
Handler class which implements InvocationHandler & manages calls to original method.
Here I have put condition, if number is greater than 100, then only original method will be called else I will return 1.0 only.
Here, I can create some cache for the results, so that if I dont have the answer in my cache, then only will call the original method.
This way I can improve the performance further.
public class Handler implements InvocationHandler {
private final Interface original;
public Handler(Interface original) {
this.original = original;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("In handler");
int num = (Integer)args[0];
if(num <= 100)
return 1d;
else
return method.invoke(original, args);
}
}
Below is the Driver class, which will be used to check the functionality of proxies here. Check, I have used 2 ways to get the proxies below -
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Proxy;
public class Driver {
public static void main(String[] args) throws InstantiationException, IllegalAccessException, IllegalArgumentException,
InvocationTargetException, NoSuchMethodException, SecurityException {
Interface original = new Original();
Handler handler = new Handler(original);
Interface f = (Interface) Proxy.newProxyInstance(Interface.class.getClassLoader(),
new Class[] { Interface.class },
handler);
System.out.println(f.findSqrRoot(121));
Class cl = Proxy.getProxyClass(Original.class.getClassLoader(), new Class[] {Interface.class});
Interface in = (Interface) cl.getConstructor(new Class[] {InvocationHandler.class}).newInstance(new Object[] { handler });
System.out.println(in.findSqrRoot(121));
}
}
In the proxy pattern, a proxy class is used to control access to another class.
The reasons for this control can vary. As one example, a proxy may avoid instantiation of an object until the object is needed.
This can be useful if the object requires a lot of time or resources to create. Another reason to use a proxy is to control access rights to an object.
A client request may require certain credentials in order to access the object.
Another reason is to log the calls to the original method.
It is also used by mocking frameworks for unit testing.
Using Java Reflection you create dynamic implementations of interfaces at runtime. You do so using the class java.lang.reflect.Proxy.
One example of proxies - Office internet.
We generally use proxies to access the internet in office & these proxies work as a filter/access to the sites being visited.
By these proxies also, admin can have the record of sites being visited by you.
Another example - Proxies during the class/lectures
We all have gone through this phase, where we used to work as a proxy for our friends during the lectures in college.
Means for attendance, that person is not required, so other person is working as a proxy for that missing person.
You create proxies using the Proxy.newProxyInstance() method. The newProxyInstance() methods takes 3 parameters:
The ClassLoader that is to "load" the dynamic proxy class.
An array of interfaces to implement.
An InvocationHandler to forward all methods calls on the proxy to.
Example Code : Note - We need interface for which we can have the proxies, on classes directly it doesn't work.
public interface Interface {
public double findSqrRoot(int num);
}
Original class is calculating the square root of the number provided to its method findSqrRoot(). Lets assume its a heavy method.
public class Original implements Interface {
public double findSqrRoot(int num) {
return Math.sqrt(num);
}
}
Handler class which implements InvocationHandler & manages calls to original method.
Here I have put condition, if number is greater than 100, then only original method will be called else I will return 1.0 only.
Here, I can create some cache for the results, so that if I dont have the answer in my cache, then only will call the original method.
This way I can improve the performance further.
public class Handler implements InvocationHandler {
private final Interface original;
public Handler(Interface original) {
this.original = original;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("In handler");
int num = (Integer)args[0];
if(num <= 100)
return 1d;
else
return method.invoke(original, args);
}
}
Below is the Driver class, which will be used to check the functionality of proxies here. Check, I have used 2 ways to get the proxies below -
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Proxy;
public class Driver {
public static void main(String[] args) throws InstantiationException, IllegalAccessException, IllegalArgumentException,
InvocationTargetException, NoSuchMethodException, SecurityException {
Interface original = new Original();
Handler handler = new Handler(original);
Interface f = (Interface) Proxy.newProxyInstance(Interface.class.getClassLoader(),
new Class[] { Interface.class },
handler);
System.out.println(f.findSqrRoot(121));
Class cl = Proxy.getProxyClass(Original.class.getClassLoader(), new Class[] {Interface.class});
Interface in = (Interface) cl.getConstructor(new Class[] {InvocationHandler.class}).newInstance(new Object[] { handler });
System.out.println(in.findSqrRoot(121));
}
}