“This is the 10th day of my participation in the Gwen Challenge in November. Check out the details: The Last Gwen Challenge in 2021.”
A common implementation of dynamic proxies is reflection. Reflection mechanism refers to the ability of program to access, detect and modify its own state or behavior during running. With reflection, we can call any class object, as well as the properties and methods contained in the class object.
But there is more to dynamic proxies than reflection. For example, dynamic proxies can be implemented using CGLib, which is based on ASM (a Java bytecode manipulation framework) rather than reflection. Simply put, dynamic proxy is a mode of behavior, and reflection or ASM is just a means of implementing it.
The differences between JDK Proxy and CGLib are as follows:
- JDK Proxy is a built-in function of the Java language, which does not need to load third-party classes.
- Java provides stable support for JDK Proxies and will continue to upgrade and update JDK Proxies. For example, JDK Proxy performance in Java 8 has improved significantly compared to previous versions.
- JDK Proxy is implemented by interceptor plus reflection;
- JDK Proxies can only Proxy classes that inherit interfaces;
- JDK Proxy is simple to implement and invoke;
- CGLib is a third-party tool based on ASM with high performance.
- CGLib does not require an interface; it makes calls by implementing subclasses.
Analysis of the
1. Use and code analysis of JDK Proxy and CGLib
JDK Proxy dynamic Proxy implementation
JDK Proxy dynamic Proxy implementation does not need to reference third-party classes, just need to implement the InvocationHandler interface, override the invoke() method, the entire implementation code is as follows:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/** * JDK Proxy examples */
public class ProxyExample {
static interface Car {
void running(a);
}
static class Bus implements Car {
@Override
public void running(a) {
System.out.println("The bus is running."); }}static class Taxi implements Car {
@Override
public void running(a) {
System.out.println("The taxi is running."); }}/** * JDK Proxy */
static class JDKProxy implements InvocationHandler {
private Object target; // Proxy object
// Get the proxy object
public Object getInstance(Object target) {
this.target = target;
// Get the proxy object
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
}
/** * Execute the proxy method *@paramProxy Proxy object *@paramMethod Proxy method *@paramParameter * to the args method@return
* @throws InvocationTargetException
* @throws IllegalAccessException
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws InvocationTargetException, IllegalAccessException {
System.out.println("Business processing before dynamic proxy.");
Object result = method.invoke(target, args); // Execute the calling method (before and after this method is executed, relevant business processing can be done)
returnresult; }}public static void main(String[] args) {
// Execute JDK Proxy
JDKProxy jdkProxy = new JDKProxy();
Car carInstance = (Car) jdkProxy.getInstance(new Taxi());
carInstance.running();
}
Copy the code
The execution result of the above procedure is:
Business processing before dynamic proxy. The taxi is running.Copy the code
Invoke () method Invocation Invocation Invocation Invocation Invocation Invocation Invocation Invocation Invocation Invocation Invocation Invocation
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
Copy the code
This is because there is an important role in the dynamic proxy, the proxy, which is used to centrally manage the prostedobject. Obviously InvocationHandler is this proxy, and the invoke() method is the execution method that triggers the proxy. We have dynamic proxy capability by implementing the Invocation interface.
The implementation of additional
Before using CGLib, we need to introduce the CGLib framework in our project and add the following configuration to pom.xml:
<! -- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
Copy the code
CGLib implementation code is as follows:
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CGLibExample {
static class Car {
public void running(a) {
System.out.println("The car is running."); }}/** * CGLib proxy class */
static class CGLibProxy implements MethodInterceptor {
private Object target; // Proxy object
public Object getInstance(Object target) {
this.target = target;
Enhancer enhancer = new Enhancer();
// Set the parent class to the instance class
enhancer.setSuperclass(this.target.getClass());
// Callback method
enhancer.setCallback(this);
// Create a proxy object
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("Business processing before method call.");
Object result = methodProxy.invokeSuper(o, objects); // Perform the method call
returnresult; }}// Execute the CGLib method call
public static void main(String[] args) {
Create a CGLib proxy class
CGLibProxy proxy = new CGLibProxy();
// Initialize the proxy object
Car car = (Car) proxy.getInstance(new Car());
// Execute method
car.running();
}
Copy the code
The execution result of the above procedure is:
Business processing before method invocation. The car is running.Copy the code
It can be seen that CGLib and JDK Proxy implementation code is similar, both through the implementation of the interface of the Proxy, and then call a method to complete the dynamic Proxy, the only difference is that when CGLib initializes the proxied class, Dynamic proxying is implemented by setting the proxy object as a subclass of the propped class through the Enhancer object. Therefore, the proxyed class cannot be modified by the keyword final. If it is modified by final, an error will be reported when setting the parent class using Enhancer, and the dynamic proxy will fail to be built.
2. Lombok principle analysis
Before we dive into Lombok’s mechanics, let’s take a quick look at Lombok, a popular Java utility class that can be used to effectively solve tedious, repetitive code in code engineering. Setters, getters, toString, equals, hashCode, and so on can all be done with Lombok annotations to this method.
For example, we use a lot of setters and getters. Before Lombok, the code would look like this:
public class Person {
private Integer id;
private String name;
public Integer getId(a) {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName(a) {
return name;
}
public void setName(String name) {
this.name = name; }}Copy the code
After using Lombok, the code looks like this:
@Data
public class Person {
private Integer id;
private String name;
}
Copy the code
You can see Lombok makes the code much simpler and more elegant.
Tip: If Lombok’s Getter and Setter annotations are used in your project, then to successfully call the set or GET methods of an object during the coding phase, you need to install Lombok plug-ins in your IDE, such as the Idea plugin shown in the following figure:
The implementation of Lombok has nothing to do with reflection. Reflection is a function of introspect at runtime, but Lombok is implemented at compile time.
Going back to the Setter/Getter method, open the compiled class for Person and find the source code using Lombok’s @data annotation as follows:
Lombok generates the corresponding bytecode for us at compile time.
Lombok is based on the JSR 269: Pluggable Annotation Processing API implemented in Java 1.6, which uses a compile-time custom Annotation processor to perform the following steps:
As you can see from the flowcharts, Lombok dynamically modifies the AST at compile time, adding new code (nodes) based on its annotation handlers, after the Java source code has been abstracted into a syntax tree (AST), and then generates the final bytecode (.class) file. This is how Lombok works.
3. Dynamic proxy knowledge expansion
When interviewers ask about dynamic agents, they often ask the difference between dynamic agents and static agents. Static proxy is actually a pre-written proxy class, which can be manually written or generated using tools. But its disadvantage is that each business class has to correspond to a proxy class, which is particularly inflexibly and inconvenient, so there is a dynamic proxy.
Dynamic proxies are commonly used in RPC framework encapsulation, AOP (aspect oriented programming) implementation, JDBC connectivity, and so on.
The Spring framework uses both JDK Proxy and CGLib as dynamic proxies. Spring uses JDK Proxy when the Bean implements the interface, and CGLib when the Bean does not implement the interface. We can also specify that CGLib is mandatory in the configuration. Just add < AOP: Aspectj-autoproxy proxy-target-class=”true”/> to your Spring configuration.
summary
JDK Proxy is a dynamic Proxy built into Java language. It must implement interfaces to Proxy related classes. CGLib is an efficient dynamic Proxy class provided by a third party based on ASM. It implements dynamic proxying by implementing subclasses of proxied classes, so that proxied classes cannot use final modifiers.
In addition to JDK Proxy and CGLib, it also introduces the implementation principle of Lombok, a common tool class in Java, which has nothing to do with reflection. Finally, the dynamic proxy usage scenario and Spring dynamic proxy implementation.