Advanced
Dynamic proxy allows us to implement interfaces dynamically by handling method calls in an InvocationHandler. It allows us to intercept method calls and reroute them or add functionality dynamically or do things like security checks,logging etc.
Dynamic Proxy Usage
- AOP frameworks like Spring AOP uses dynamic proxy to address the issue of crosscutting concerns.
- Database Connection and Transaction Management
- Security
- Logging
- Performing extra checks on arguments
- Implement lazy access to costly resources
- Creating dynamic implementations of interfaces at runtime
How to Create Dynamic Proxy
We can create dynamic proxy 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.
InvocationHandler simpleClassHandler = new ProxyInvocationHandler(simpleClass);
SimpleInterface simpleInterfaceProxy = (SimpleInterface) Proxy.newProxyInstance(simpleClass.getClass().getClassLoader(),
simpleClass.getClass().getInterfaces(),simpleClassHandler);
Let us look into InvocationHandler, the real implementer.
InvocationHandler
All method calls to the dynamic proxy are forwarded to InvocationHandler implementation. we have to implement invoke method of InvocationHandler.
public Object invoke(Object proxy, Method method, Object[] args)
It processes a method invocation on a proxy instance and returns the result. It will be invoked on an invocation handler when a method is invoked on a proxy instance associated with it.
proxy: dynamic proxy object implementing the interface.
method: represents the method called on the interface the dynamic proxy implements.
args: contains the parameter values passed to the proxy when the method in the interface implemented was called.
public class ProxyInvocationHandler implements InvocationHandler {
private Object obj;
public ProxyInvocationHandler(Object obj) {
this.obj = obj;
}
public ProxyInvocationHandler() {
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
if (obj != null) {
System.out.println(“Before ” + methodName + ” invocation”);
Object result = method.invoke(obj, args);
System.out.println(“After ” + methodName + ” invocation”);
return result;
}
return null;
}
}
Let us look into few examples wherein we can make use of Dynamic Proxies.
Example 1 : Creating dynamic implementations of interfaces at runtime
Animal Interface
package com.dynamic.proxy;
public interface Animal {
public void description(String type);
}
ProxyInvocationHandler Class
package com.dynamic.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyInvocationHandler implements InvocationHandler {
private Object obj;
public Object getObj() {
return obj;
}
public void setObj(Object obj) {
this.obj = obj;
}
public ProxyInvocationHandler(Object obj) {
this.obj = obj;
}
public ProxyInvocationHandler() {
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
String methodName = method.getName();
if (proxy instanceof Animal) {
if (methodName.equals(“description”) && args != null
&& args.length == 1) {
System.out.println(“This is a ” + args[0]);
}
}
return null;
}
public static void main(String[] args) {
InvocationHandler animalHandler = new ProxyInvocationHandler();
Animal animalProxy = (Animal) Proxy.newProxyInstance(
Animal.class.getClassLoader(), new Class[] { Animal.class },
animalHandler);
animalProxy.description(“Dog”);
animalProxy.description(“Cat”);
animalProxy.description(“Tiger”);
}
}
Output:
This is a Dog
This is a Cat
This is a Tiger
Example 2 : Intercepting method calls
SimpleInterface
package com.dynamic.proxy;
public interface SimpleInterface {
public void test();
public String test1();
}
SimpleClass
package com.dynamic.proxy;
public class SimpleClass implements SimpleInterface{
public void test(){
System.out.println(“Inside test”);
}
public String test1(){
System.out.println(“Inside test1”);
return “testString”;
}
}
ProxyInvocationHandler
package com.dynamic.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyInvocationHandler implements InvocationHandler {
private Object obj;
public ProxyInvocationHandler(Object obj) {
this.obj = obj;
}
public ProxyInvocationHandler() {
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
String methodName = method.getName();
if (obj != null) {
System.out.println(“Before ” + methodName + ” invocation”);
Object result = method.invoke(obj, args);
System.out.println(“After ” + methodName + ” invocation”);
return result;
}
return null;
}
public static void main(String[] args) {
SimpleClass simpleClass = new SimpleClass();
InvocationHandler simpleClassHandler = new ProxyInvocationHandler(
simpleClass);
SimpleInterface simpleInterfaceProxy = (SimpleInterface) Proxy
.newProxyInstance(simpleClass.getClass().getClassLoader(),
simpleClass.getClass().getInterfaces(),
simpleClassHandler);
simpleInterfaceProxy.test();
System.out.println(“Return value:” + simpleInterfaceProxy.test1());
}
}
Output:
Before test invocation
Inside test
After test invocation
Before test1 invocation
Inside test1
After test1 invocation
Return value:testString
Awesome Gyan bhai. Great explanation in such simple words. Thanks a lot for the detailed explanation.