Java dynamic proxy
"TLDR: This article introduces two implementation methods of Java dynamic proxy: JDK dynamic proxy and CGLIB dynamic proxy. JDK dynamic proxy is implemented through the `InvocationHandler` interface and the `Proxy` class, which avoids manual writing of proxy classes and improves development efficiency. CGLIB dynamic proxy generates subclasses in the bytecode of the target class through the reflection mechanism and rewrites methods to implement the proxy. It is suitable for scenarios where various classes need to be completely enhanced."
Dynamic proxies are very commonly used in Java, but they are mainly used in frameworks. Once mastered, it is better for understanding the framework. Dynamic proxies are rarely used directly in daily development.
Dynamic proxy
First, what is a proxy? When we want to add new functions to a certain class, but do not want to modify the original class, we can write a proxy class to handle it. This is a static proxy. If we modify it directly in the original class, it may become longer as we modify it, making the internal logic of the function or class extremely complex, which is not conducive to maintenance.
Just imagine, in a business code that has been written, for example, there is a function called updateDataBase, and we hope to add the log printing function. Adding more log code inside updateDataBase will not look good, causing the real business code to be submerged in the log code, resulting in poor maintainability.
Why need dynamic proxy? Why dynamic?
-
Static proxy requires manually writing a proxy class for each target class. One or two is fine. If there are thousands of target classes and you want to add logging logic to them all, writing a proxy class is very painful and time-consuming.
-
If you want to modify the proxy logic, not only add logging function, but also add other functions, then modifying the written proxy class is a time-consuming and labor-intensive task
JDK dynamic proxy
A common proxy implementation method is through JDK. You only need to use the InvocationHandler interface and the Proxy class to avoid manually writing proxy classes.
Proxy is used to generate proxy class objects. It can be regarded as the factory method of the proxy class. The most frequently used method is newProxyInstance(). Use this method to generate a proxy object.
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
...
}
-
loader: class loader. Used to load proxy objects
-
interfaces: some interfaces implemented by the proxy class
-
h: Object that implements the
InvocationHandlerinterface
Among them, InvocationHandler is used to customize the processing logic. When we call the method of the proxy class, it will be forwarded to the invoke method of the InvocationHandler class, and the method of the target class will be called inside the invoke method.
public interface InvocationHandler {
/**
* When you use a proxy object to call a method, this method will actually be called.
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
Specifically, in the invoke method of the InvocationHandler interface class, three parameters need to be passed in:
-
proxy: dynamically generated proxy class
-
method:
-
args: parameters
Implementation steps
-
Define an interface and its implementation class
-
Customize
InvocationHandlerand overrideinvokemethod -
Create a proxy object through
Proxy
//Create an interface
public interface Celebrity {
void performActivity(String activity);
}
//The real target class, that is, the target object that needs to be proxied
public class ZhangSan implements Celebrity {
@Override
public void performActivity(String activity) {
System.out.println("Zhang San is doing activities: " + activity);
}
}
//The proxy class needs to implement the invoke method of the InvocationHandler interface class
public class CelebrityAgent implements InvocationHandler {
private Celebrity celebrity;
public CelebrityAgent(Celebrity celebrity) {
this.celebrity = celebrity;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("The broker is arranging activities...");
Object result = method.invoke(celebrity, args);
System.out.println("Activity arrangement completed!");
return result;
}
}
// Use Proxy to dynamically create a proxy class
public class DynamicProxyDemo {
public static void main(String[] args) {
Celebrity realCelebrity = new ZhangSan(); // This is our real celebrity
Celebrity agentCelebrity = (Celebrity) Proxy.newProxyInstance(
Celebrity.class.getClassLoader(),
new Class[]{Celebrity.class},
newCelebrityAgent(realCelebrity)
);
agentCelebrity.performActivity("Signing Session"); // Here we arrange activities through the "agent"
}
}
Then, this implements dynamic proxy through jdk.
Going back to the shortcomings of the static proxy mentioned before, in the dynamic proxy, you only need to define the CelebrityAgent proxy class that implements the InvocationHandler once, and you can create a proxy class for all target classes (such as Zhang San, Li Si, Wang Wu). In the static proxy, you need to hand-write three proxy classes. In addition, if we want to modify the agent's logic, we can modify it directly in CelebrityAgent, which is very convenient.
CGLIB dynamic proxy
The principle of Jdk dynamic proxy is based on interface and reflection, which generates an enhanced proxy class for the target interface and calls the original method of the target class internally through reflection. Therefore, Jdk dynamic proxy is strongly bound to the interface, which greatly reduces the scope of use during programming. For example, we hope to add the logging function to all the Controller methods of the Spring Boot application we have written, but these Controller methods do not implement a unified interface at all, making it difficult to implement JDK dynamic proxy.
In order to completely let yourself go and enhance various classes without restraint, Spring focuses on the dynamic proxy of CGLIB, which realizes proxy by automatically generating subclasses of the target class and rewriting the target method. Specifically, modify the bytecode of the target class to generate a subclass (proxy class)
In terms of efficiency between the two, JDK dynamic proxy is better in most cases.