Proxy is used to intercept OR to surrogate OR change existing functionality of target object without modifying it, but same thing we can achieve using normal java class by inheriting target class & overriding it's methods.
Then what is exact usage of it ? How it is different & efficient from normal java class ?
4 Answers
Proxy is just a name given to a class following the proxy design pattern. It is a normal java class.
For further reading, check Proxy pattern in Java
2 Comments
You can implement proxies with ordinary classes, e.g.
Iterable<String> i = Arrays.asList("hello", "world");
Iterable<String> proxy = new Iterable<String>() {
@Override
public Iterator<String> iterator() {
System.out.println("Entering .iterator()");
try {
return i.iterator();
}
finally {
System.out.println("Exiting .iterator()");
}
}
};
for(String s: proxy) {
System.out.println(s);
}
Entering .iterator()
Exiting .iterator()
hello
world
The advantage of dynamic proxy generators, like java.lang.reflect.Proxy, are that they allow to handle an arbitrary number of interface methods with a single handler method and don’t require hardcoding of the interfaces to implement. The interfaces do not even need to exist at compile-time.
For example:
List<String> i = Arrays.asList("hello", "world");
final class MyInvocationHandler implements InvocationHandler {
private final Object target;
public MyInvocationHandler(Object t) { target = t; }
@Override public Object invoke(
Object proxy, Method method, Object[] args) throws Throwable {
String name = method.getDeclaringClass().getSimpleName()+'.'+method.getName();
System.out.println(" Entering "+name);
try {
Object o = method.invoke(target, args);
final Class<?> rt = method.getReturnType();
if(rt != Void.TYPE) System.out.println(" => "+o);
return o == null || !rt.isInterface()? o:
Proxy.newProxyInstance(null,new Class<?>[]{rt},new MyInvocationHandler(o));
}
finally {
System.out.println(" Exiting "+name);
}
}
}
List<String> proxy = (List<String>)
Proxy.newProxyInstance(null, new Class<?>[]{List.class}, new MyInvocationHandler(i));
System.out.println("*** for loop:");
for(String s: proxy) System.out.println(s);
System.out.println();
System.out.println("*** stream:");
proxy.stream().forEach(System.out::println);
System.out.println();
System.out.println("*** parallel stream:");
StreamSupport.stream(proxy.spliterator(), true).forEach(System.out::println);
*** for loop:
Entering List.iterator
=> java.util.Arrays$ArrayItr@5cb0d902
Exiting List.iterator
Entering Iterator.hasNext
=> true
Exiting Iterator.hasNext
Entering Iterator.next
=> hello
Exiting Iterator.next
hello
Entering Iterator.hasNext
=> true
Exiting Iterator.hasNext
Entering Iterator.next
=> world
Exiting Iterator.next
world
Entering Iterator.hasNext
=> false
Exiting Iterator.hasNext
*** stream:
Entering Collection.stream
=> java.util.stream.ReferencePipeline$Head@443b7951
Exiting Collection.stream
Entering Stream.forEach
hello
world
Exiting Stream.forEach
*** parallel stream:
Entering List.spliterator
=> java.util.Spliterators$ArraySpliterator@5d6f64b1
Exiting List.spliterator
Entering Spliterator.characteristics
=> 16464
Exiting Spliterator.characteristics
Entering Spliterator.estimateSize
=> 2
Exiting Spliterator.estimateSize
Entering Spliterator.trySplit
=> java.util.Spliterators$ArraySpliterator@6aa8ceb6
Exiting Spliterator.trySplit
Entering Spliterator.estimateSize
=> 1
Exiting Spliterator.estimateSize
Entering Spliterator.getExactSizeIfKnown
=> 1
Exiting Spliterator.getExactSizeIfKnown
Entering Spliterator.forEachRemaining
world
Exiting Spliterator.forEachRemaining
Entering Spliterator.estimateSize
=> 1
Exiting Spliterator.estimateSize
Entering Spliterator.getExactSizeIfKnown
=> 1
Exiting Spliterator.getExactSizeIfKnown
Entering Spliterator.forEachRemaining
hello
Exiting Spliterator.forEachRemaining
This demonstrates how a single handler method can decorate the List interface with two dozen methods and dynamically decorate return values when the return type is an interface, decorating four interfaces in the example code, and it would decorate more when you change the test case.
Just try to implement this with ordinary implementations of those interfaces and you’ll immediately recognize the advantage of the dynamic proxy generator. Not to speak of the ability to adapt to new interfaces when the show up…
A different use case would be dynamic component binding. This may look like this:
JButton component = new JButton("example");
String event = "action";
BeanInfo bi = Introspector.getBeanInfo(component.getClass());
for(EventSetDescriptor ed: bi.getEventSetDescriptors()) {
if(ed.getName().equals(event)) {
System.out.println("listening to "+ed.getDisplayName());
final Class<?> listener = ed.getListenerType();
ed.getAddListenerMethod().invoke(component,
Proxy.newProxyInstance(listener.getClassLoader(), new Class<?>[]{listener},
new InvocationHandler() {
@Override
public Object invoke(
Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("event "+method.getName()+Arrays.toString(args));
return null;
}
})
);
}
}
component.doClick();
listening to action
event actionPerformed[java.awt.event.ActionEvent[ACTION_PERFORMED,cmd=example,when=1579865741229,modifiers=] on javax.swing.JButton[,0,0,0x0,invalid,alignmentX=0.0,alignmentY=0.5,border=javax.swing.plaf.BorderUIResource$CompoundBorderUIResource@5fdba6f9,flags=296,maximumSize=,minimumSize=,preferredSize=,defaultIcon=,disabledIcon=,disabledSelectedIcon=,margin=javax.swing.plaf.InsetsUIResource[top=2,left=14,bottom=2,right=14],paintBorder=true,paintFocus=true,pressedIcon=,rolloverEnabled=true,rolloverIcon=,rolloverSelectedIcon=,selectedIcon=,text=example,defaultCapable=true]]
There’s already java.beans.EventHandler providing exactly this functionality for the common use case of setting another component’s property in response to an event.
Comments
Proxy class is a class that is operating like an interface for another class or a group of classes.
It can be considered like a Smart Interface
Proxy design pattern can be used to add some decision-making (access control, rate limiting, environment setting, etc) before instantiating an object.
One of the most popular examples of Proxy pattern can be found in Browser UI test automation.
Example use case of proxy pattern
Suppose that there are 3 browser objects, defined by 3 different classes - InternetExplorer, Firefox, Chrome.
If the requirement is to run a test case on a browser selected at runtime, then the Proxy pattern can become helpful.
A Proxy class called 'Browser' that takes 'browser name' as the input and instantiates the desired browser at runtime can be a great solution in this scenario.