Introduction to proxy Mode
The proxy pattern is a design pattern. As the name implies, it is the proxy of a thing, through which all external access or operations to the thing go. For example, if you have a legal dispute, you must go to a lawyer. For the lawyer, you are his client. For you, the lawyer is your agent.
In the programming world, we can describe the relationship between delegate classes and proxy classes
The role of the proxy mode: provides additional access to the delegate class, that is, through the proxy class to access the delegate class, so that you can provide additional functional operations without modifying the delegate class, thus extending the functionality of the delegate class.
In a nutshell, the proxy pattern enhances the delegate class by setting up an intermediate proxy to control all access to the delegate class.
In order to maintain consistency of behavior, proxy classes and delegate classes often implement the same interface, so there is no difference between them to visitors. Through the middle layer of proxy class, it can effectively control the direct access to the delegate class object, and also can hide and protect the delegate class object well. At the same time, it also reserves space for the implementation of different control strategies, so as to obtain greater flexibility in the design
Classification of proxy patterns
Before we go into the classification of proxy patterns, let’s prepare some classes and interfaces
// The DataService interface provides the save method
public interface DataService {
public void save(a);
}
The DataServiceImpl class implements the DataService interface and overwrites the save method
public class DataServiceImpl implements DataService{
@Override
public void save(a) {
System.out.println("DataServiceImpl method save is called");
}
}
Copy the code
Static proxy (wrapping)
/ / the proxy class
public class DataServiceProxy implements DataService{
/ / the delegate class
DataService dataService = new DataServiceImpl();
@Override
public void save(a) {
System.out.println("DataServiceProxy method save is called");
dataService.save();
}
}
Copy the code
For the purposes of this code analysis, DataServiceProxy is the proxy class and DataServiceImpl is the delegate class
Advantages and disadvantages of static proxy:
- advantages
- You can extend the functionality of a delegate class without modifying it
- disadvantages
- The proxy class and the delegate class implement the same interface
- Hardcoded, once the delegate class adds methods, the proxy class also needs to change
JDK dynamic proxy
Use JDK apis to dynamically build in-memory instance objects of proxy classes
A dynamic proxy class (hereinafter referred to as a proxy class) is a class that implements a list of interfaces specified at run time when the class is created and has the behavior described below:
- A proxy interface is an interface implemented by a proxy class.
- A proxy instance is an instance of a proxy class.
- Each proxy instance has an associated InvocationHandler object that implements the interface InvocationHandler. A Method call on a proxy instance through one of the proxy interfaces will be assigned to the Invoke Method of the instance’s call handler, passing the proxy instance, a java.lang.Reflect. Method Object that identifies the calling Method, and an array of type Object containing the parameters. The invocation handler handles the encoded method call in an appropriate manner, and the result it returns is returned as the result of the method call on the proxy instance
To understand the mechanics of Java dynamic proxies, you first need to understand the following classes or interfaces:
java.lang.reflect.Proxy
This is the main class of Java’s dynamic proxy mechanism, which provides a set of static methods to dynamically generate proxy classes and their objects for a set of interfaces
Static method of Proxy
// Method 1: This method is used to get the calling handler associated with the specified proxy object
static InvocationHandler getInvocationHandler(Object proxy)
// Method 2: This method is used to get the class object associated with a dynamic proxy class that specifies a class loader and a set of interfaces
static Class getProxyClass(ClassLoader loader, Class[] interfaces)
// Method 3: This method is used to determine whether the specified class object is a dynamic proxy class
static boolean isProxyClass(Class cl)
// Method 4: This method is used to generate dynamic proxy class instances for the specified class loader, a set of interfaces, and the calling handler
static Object newProxyInstance(ClassLoader loader, Class[] interfaces,
InvocationHandler h)
Copy the code
java.lang.reflect.InvocationHandler
This is the call handler interface, which has a custom oneinvoke
Method that centrally handles method calls on dynamic proxy class objects, typically in which proxy access to delegate classes is implemented
The core method of InvocationHandler
/ * *
* This method is responsible for centrally handling all method calls on a dynamic proxy class. The first argument is both, and the second argument is
* proxy: proxy class instance
* method: The method object to be called
* args: call parameters
* The invocation handler preprocesses or dispatches execution to the delegate class instance based on these three parameters
* /
Object invoke(Object proxy, Method method, Object[] args)
Copy the code
Each time a dynamic Proxy class object is generated, you need to specify a call handler object that implements this interface (see third parameter of Proxy static method 4).
java.lang.ClassLoader
This is the classloader that loads the bytecode into the Java Virtual Machine (JVM) and defines class objects for it (Class
) before the class can be used. throughProxy.newProxyInstance
The generated dynamic proxy classes also need to be loaded by the class loader before they can be used.The only difference is that its bytecode is dynamically generated by the JVM at runtime rather than stored in some concrete.class file
Dynamic proxy mechanism and its characteristics
Let’s first look at how to use Java dynamic proxies. There are four steps as follows:
- By implementing
InvocationHandler
Interface and implementationinvoke
Method to create your own call handler - By providing
Proxy
Class specifiedClassLoader
The objects andA set of interface
To create dynamic proxy classes - A constructor for a dynamic proxy class whose only argument type is the calling processor interface type is obtained through reflection
- A dynamic proxy class instance is created through a constructor that calls the processor object passed in as a parameter
// InvocationHandlerImpl implements the InvocationHandler interface and dispatches method calls from the proxy class to the delegate class
// It usually contains a reference to the delegate class instance inside, which is used to actually perform the method call dispatched forward
InvocationHandler handler = newInvocationHandlerImpl(..) ;
// Dynamically create a Proxy class object for a set of interfaces, including the Interface
Class clazz = Proxy.getProxyClass(classLoader, new Class[] { Interface.class, ... });
// Get the constructor object from the generated class object by reflection
Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class });
// Create a dynamic proxy class instance with the constructor object
Interface Proxy = (Interface)constructor.newInstance(new Object[] { handler });
Copy the code
The actual process is simpler, because the static method newProxyInstance of Proxy has encapsulated the process from Step 2 to Step 4 for us, so the simplified process is as follows
// InvocationHandlerImpl implements the InvocationHandler interface and dispatches method calls from the proxy class to the delegate class
InvocationHandler handler = newInvocationHandlerImpl(..) ;
// Create a dynamic Proxy class instance directly through Proxy
Interface proxy = (Interface)Proxy.newProxyInstance( classLoader,
new Class[] { Interface.class },
handler );
Copy the code
Specific code implementation:
Invoke handler class
public class DataServiceInvovationHandler implements InvocationHandler {
DataService dataService;
public DataServiceInvovationHandler(DataService dataService) {
this.dataService = dataService;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method.getDeclaringClass() + "" + method.getName() + "() is called");
method.invoke(dataService, args);
return null;
}
}
Copy the code
Dynamic proxy class
public class DataServiceDynamicProxy {
DataService dataService = new DataServiceImpl();
public void save(a) {
DataService dataServiceDynamicProxy =
(DataService) Proxy.newProxyInstance(dataService.getClass().getClassLoader(), new Class[]{DataService.class}, new DataServiceInvovationHandler(dataService));
dataServiceDynamicProxy.save();
}
}
Copy the code
The differences between static and dynamic proxies are as follows:
- The static proxy is implemented at compile time, when the proxy class is an actual class file
- Dynamic proxies are dynamically generated at run time, meaning that there are no actual class files after compilation, but instead, class bytecodes are dynamically generated at run time and loaded into the JVM
Additional agent
Cglib (Code Generation Library) is a third party Code Generation class Library. The runtime dynamically generates a subclass object in memory to extend the function of the target object
Additional features
- One limitation of the JDK’s dynamic proxy is that objects using dynamic proxies must implement one or more interfaces. If you want to proxy classes that do not implement interfaces, you can use the CGLIB implementation.
- CGLIB is a powerful, high-performance code generation package that extends Java classes and implements Java interfaces at run time. It is widely used by many AOP frameworks, such as Spring AOP and Dynaop, to provide methods for them
interception
(Intercept). - At the bottom of the CGLIB package is the ability to transform bytecode and generate new classes by using ASM, a small and fast bytecode processing framework. Direct use of ASM is discouraged because it requires familiarity with the JVM’s internal structures, including the class file format and instruction set.
The biggest difference between Cglib and dynamic proxies is this
- Objects that use dynamic proxies must implement one or more interfaces
- use
cglib
The object of proxy does not need to implement interface, so as to achieve no intrusion of proxy class
Cglib jar: maven maven jar: maven jar: maven jar: maven JAR: maven JAR: MAVEN JAR
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.5</version>
</dependency>
Copy the code
Code implementation
Delegate class
public class DataService {
public void save(a) {
System.out.println("class DataService method save is called");
}
}
Copy the code
The proxy class
public class CglibProxy implements MethodInterceptor {
private Object target; / / the delegate class
public CglibProxy(Object target) {
this.target = target;
}
// Generate a proxy object for the delegate class
public Object getProxyInstance(a) {
/ / tools
Enhancer enhancer = new Enhancer();
// Set the delegate class as the parent
enhancer.setSuperclass(target.getClass());
// Set the callback function
enhancer.setCallback(this);
// Create a subclass object proxy
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("DataService cglib proxy start...");
// Execute delegate class methods through reflection
method.invoke(target, objects);
System.out.println("DataService cglib proxy end...");
return null;
}
}
Copy the code
The test results
conclusion
- Static proxy implementation is simpler, as long as the proxy object wraps the target object to achieve enhanced functionality, but static proxy can only serve one target object, if there are too many target objects, many proxy classes will be generated.
- The JDK dynamic proxy requires the target object to implement the business interface; the proxy class only needs to implement it
InvocationHandler
Interface. - Static proxies are generated at compile time
class
Bytecode file, can be directly used, high efficiency. - Dynamic proxies must be implemented
InvocationHandler
Interfaces, by reflecting proxy methods, consume more system performance but can reduce the number of proxy classes and be more flexible to use. cglib
The proxy does not need to implement an interface and implements the proxy by generating bytecode like, which is slightly faster than reflection and does not have a performance problem, butcglib
Will inherit the target object, need to override the method, so the target object cannot befinal
class