proxy pattern
The proxy pattern is the most commonly used java design pattern, which is characterized by the same interface between the proxy class and the delegate class. The proxy class is mainly responsible for preprocessing messages for the delegate class, filtering messages, forwarding messages to the delegate class, and post-processing messages. There is usually an association relationship between proxy class and delegate class. The object of a proxy class is associated with the object of a delegate class. The object of the proxy class itself does not really implement the service, but provides a specific service by calling the relevant methods of the object of the delegate class. In short, when accessing the actual object, we access it through the proxy object. The proxy mode introduces a certain degree of indirectness when accessing the actual object, because this indirectness can be added for a variety of purposes
Static proxy
Static proxy: it is created by programmers or automatically generated by specific tools, that is, the interface, proxy class and proxy class have been determined at compile time. Before the program runs, the. Class file of the agent class has been generated.
Simple implementation
According to the class diagram of the agent mode above, write a simple example of static agent: if a class wants to pay class fees to the teacher, but they all pass their money to the teacher through the monitor. Here, the monitor acts as an agent for students to pay class fees, and the monitor is the agent of students.
1. Determine the specific behavior of creating an interface
First, we create a Person interface. This interface is the public interface for students (proxy class) and monitor (proxy class). They all have the behavior of paying shift fees. In this way, the class fee for students can be implemented by the monitor.
/** * Create Person interface */ public interface Person { //Turn in the shift fee void giveMoney(); }
- 2. The proxy object implements the interface and completes the specific business logic
The student class implements the Person interface. Student can specifically implement the action of shift handover fee:
public class Student implements Person { private String name; public Student(String name) { this.name = name; } @Override public void giveMoney() { System.out.println(name + "Shift handover fee: 50 yuan"); } }
3. The agent class implements the interface to complete the pre-processing messages, filtering messages, forwarding messages to the delegate class, and post-processing messages of the delegate class.
StudentsProxy class, which also implements the Person interface, but also holds a student class object. Since the Pearson interface is implemented and a student object is held at the same time, he can execute the shift fee (execute the giveMoney() method) on behalf of the student class object.
/** * The student agent class also implements the Person interface to save a student entity, which can act as a proxy for students * @author Gonjan * */ public class StudentsProxy implements Person{ //Represented student Student stu; public StudentsProxy(Person stu) { // Delegate student objects only if(stu.getClass() == Student.class) { this.stu = (Student)stu; } } //Delegate the shift fee and call the shift fee behavior of the delegated student public void giveMoney() { stu.giveMoney(); } }
Client operation and analysis
public class StaticProxyTest { public static void main(String[] args) { //Zhang San, the student being represented, handed over his class fee to the agent monitor (monitor) for completion Person zhangsan = new Student("Zhang San"); //Generate a proxy object and pass Zhang San to the proxy object Person monitor = new StudentsProxy(zhangsan); //Shift handover fee for the agent of the monitor monitor.giveMoney(); } }
Instead of directly implementing the shift fee through Zhang San (the agent), it is implemented through the monitor (the agent). This is the agent mode.
The main of proxy mode is to have a public interface (Person), a specific class (Student) and a proxy class (StudentsProxy). The proxy class holds the instance of the specific class and executes the instance method of the specific class on behalf of it. As mentioned above, the proxy mode introduces a certain degree of indirection when accessing actual objects, because this indirection can be used for a variety of purposes. The indirectness here means that we do not directly call the methods of the actual object, so we can add some other uses in the proxy process. For example, before joining the monitor to help Zhang San pay the shift fee, I want to reflect that Zhang San has made great progress in his recent study, which can be easily achieved through the agent mode:
public class StudentsProxy implements Person{ //Represented student Student stu; public StudentsProxy(Person stu) { // Delegate student objects only if(stu.getClass() == Student.class) { this.stu = (Student)stu; } } //Delegate the shift fee and call the shift fee behavior of the delegated student public void giveMoney() { System.out.println("Zhang San has made progress in his study recently!"); stu.giveMoney(); } }
Just perform other operations before Zhang San's shift fee in the agent class. This operation is also a great advantage of using proxy mode. The most straightforward is aspect oriented programming (AOP) in Spring. We can perform some operations before and after a pointcut. This pointcut is a method. The class of these methods must be proxied, and some other operations are cut in during the proxy process.
Dynamic agent
The proxy method created by the proxy class when the program is running is called dynamic proxy.
In our static proxy example above, the proxy class (studentProxy) is self-defined and has been compiled before the program runs. However, the dynamic proxy class is not defined in Java code, but dynamically generated at runtime according to our "instructions" in Java code. Compared with static proxy, the advantage of dynamic proxy is that it can easily handle the functions of proxy classes in a unified way without modifying the methods in each proxy class.
The use of dynamic agents in business is generally to add preprocessing or subsequent operations to the methods to be implemented, but does not interfere with the normal business of the implementation class, and separates some basic business from the main business logic. spring's aop is based on dynamic agent.
jdk dynamic agent
jdk's own dynamic agent
Two classes need to be implemented
invocationHandler Proxy
Implementation process
- 1. Determine the specific behavior of creating an interface
First, we create a Person interface. This interface is the public interface for students (proxy class) and monitor (proxy class). They all have the behavior of paying shift fees. In this way, the class fee for students can be implemented by the monitor.
/** * Create Person interface */ public interface Person { //Turn in the shift fee void giveMoney(); }
- 2. The proxy object implements the interface and completes the specific business logic (some methods are added here for detection, and dynamic proxy is used later for differentiation)
The Student class implements the Person interface.
public class Student implements Person { private String name; public Student(String name) { this.name = name; } @Override public void giveMoney() { try { //Suppose it took a second to count the money Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(name + "Shift handover fee: 50 yuan"); } }
A tool class for detecting execution time is defined. The start method is invoked before any method is executed. After the execution of the finsh method, the running time of the method can be calculated. This is also the simplest method for execution time detection.
public class MonitorUtil { private static ThreadLocal<Long> tl = new ThreadLocal<>(); public static void start() { tl.set(System.currentTimeMillis()); } //Printing time at end public static void finish(String methodName) { long finishTime = System.currentTimeMillis(); System.out.println(methodName + "Method time consuming" + (finishTime - tl.get()) + "ms"); } }
3. The agent class implements the interface to complete the pre-processing messages, filtering messages, forwarding messages to the delegate class, and post-processing messages of the delegate class.
Create StuInvocationHandler class and implement InvocationHandler interface. This class holds an instance target of the proxy object. There is an invoke method in InvocationHandler. All methods that execute proxy objects will be replaced with the invoke method. Execute the corresponding method of the proxy object target in the invoke method. In the proxy process, we add our own other processing before actually executing the methods of the proxy object. This is also the main principle of AOP implementation in Spring. It also involves a very important basic knowledge about java reflection.
public class StuInvocationHandler<T> implements InvocationHandler { //Proxied object held by invocationHandler T target; public StuInvocationHandler(T target) { this.target = target; } /** * proxy:Represents a dynamic proxy object * method: Represents the method being executed * args: Represents the argument passed in when the target method is called */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Agent execution" +method.getName() + "method"); //The monitoring method is inserted in the agent process, and the calculation of this method is time-consuming MonitorUtil.start(); Object result = method.invoke(target, args); MonitorUtil.finish(method.getName()); return result; } }
- 4. Client operation and analysis
public class ProxyTest { public static void main(String[] args) { //Create an instance object, which is the proxy object Person zhangsan = new Student("Zhang San"); //Create an InvocationHandler associated with the proxy object InvocationHandler stuHandler = new StuInvocationHandler<Person>(zhangsan); //Create a proxy object stuProxy to proxy zhangsan. Each execution method of the proxy object will replace the invoke method in the Invocation Person stuProxy = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, stuHandler); //Method of executing shift fee by agent stuProxy.giveMoney(); } }
Zhang San created a student who needs to be proxied and passed the zhangsan object to stuHandler. When we created the proxy object stuProxy, we took stuHandler as a parameter. As mentioned above, all methods executing the proxy object will be replaced with the invoke method, that is, the invoke method in StuInvocationHandler will be executed finally.
The advantage of dynamic proxy is that it can easily handle the functions of proxy classes uniformly without modifying the methods in each proxy class. Because all the methods executed by the proxy are called through the invoke method in the InvocationHandler, we can perform the same operation on all the proxy methods as long as we handle them uniformly in the invoke method. For example, in the method timing here, all the methods executed by the proxy object will be timed. However, I only made a small amount of code.
The process of dynamic proxy and the relationship between proxy object and proxy object are not as clear and clear as static proxy. In the process of dynamic proxy, we do not actually see the proxy class, nor do we clearly see the specific appearance of the proxy class. In addition, the proxy object and proxy object in dynamic proxy are completed through InvocationHandler. What is the specific operation, Why do all methods executed by proxy objects execute through the invoke method in InvocationHandler. With these problems, we need to briefly analyze the source code of java Dynamic agent to find out the reason.
principle
jdk generates a proxy class named $Proxy0 (0 after this name is the number, and multiple proxy classes will increase at one time). This class file is placed in memory. When we create a proxy object, we obtain the construction method of this class through reflection, and then create a proxy instance. Through the view of the generated proxy class source code, we can easily see the specific process of dynamic proxy implementation.
We can think of InvocationHandler as an intermediary class, and the intermediary class holds a proxy object and invokes the corresponding method of the proxy object in the invoke method. Hold the reference of the proxy object through aggregation, and finally turn the external call to invoke into the call to the proxy object.
When the proxy class calls its own method, it calls the invoke method of the mediation class object through its own mediation class object, so as to achieve the proxy to execute the method of the proxy object. In other words, the dynamic agent realizes the specific agent function through the intermediary class.
Summary: the generated Proxy class: $Proxy0 extends Proxy implements Person. We can see that the Proxy class inherits the Proxy class, so it determines that the Java dynamic Proxy can only Proxy the interface. The inheritance mechanism of Java doomed these dynamic Proxy classes to be unable to implement the dynamic Proxy to the class. The above example of dynamic agent is actually a simple implementation of AOP. It is processed before and after the method of the target object is executed, and the time consumption of the method is counted. Spring's AOP implementation actually uses Proxy and InvocationHandler.
InvocationHandler interface and Proxy class
InvocationHandler interface is an interface implemented by the calling handler of proxy proxy instance. Each proxy instance has an associated calling handler; When a method is called by a proxy instance, the method call is encoded and dispatched to the invoke method of the calling handler.
See the description of InvocationHandler interface in the official document:
{@code InvocationHandler} is the interface implemented by the <i>invocation handler</i> of a proxy instance. <p>Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the {@code invoke} method of its invocation handler.
When we call a method through a dynamic proxy object, the call of this method will be forwarded to the invoke method implementing the InvocationHandler interface class to call, as shown in the following invoke method:
/** * proxy:The real proxy object of proxy class proxy is com.sun.proxy.$Proxy0 * method:The Method object of the real Method of an object we want to call * args:Refers to the parameters passed by the proxy object method */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
Proxy class is a class used to create a proxy object. It provides many methods, but the most commonly used method is newProxyInstance.
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
The function of this method is to create a proxy class object, which receives three parameters. Let's look at the meaning of the following parameters:
loader: a classloader object that defines which classloader object loads the generated proxy class
Interfaces: an array of interface objects, which indicates what kind of interfaces we will provide to our proxy object. If we provide such an array of interface objects, we declare that the proxy class implements these interfaces, and the proxy class can call all the methods declared in the interface.
h: An InvocationHandler object indicates which InvocationHandler object will be associated with when the method is called by the dynamic proxy object, and finally called by it.
CGLIB dynamic proxy
JDK dynamic Proxy must implement an interface and can only Proxy interface methods, because JDK Proxy needs to inherit a Proxy class. Due to the single inheritance mechanism of Java, the Proxy class cannot inherit the functions of the parent class and can only implement the interface
//Import jar package <dependency> <groupId>org.sonatype.sisu.inject</groupId> <artifactId>cglib</artifactId> <version>3.1.1</version> </dependency>
/** * User management interface */ public interface UserManager { //New user abstraction method void addUser(String userName, String password); //Delete user abstract method void delUser(String userName); }
/** * User management implementation class, which implements the user management interface (the implementation class of the proxy) */ public class UserManagerImpl implements UserManager{ //Override user add method @Override public void addUser(String userName, String password) { System.out.println("The method added by the user has been called!"); System.out.println("Incoming parameters:\nuserName = " + userName +", password = " + password); } //Override delete user method @Override public void delUser(String userName) { System.out.println("Deleted method called!"); System.out.println("Incoming parameters:\nuserName = "+userName); } }
JDK agent implementation: JDK dynamic agent implements InvocationHandler interface
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * JDK Dynamic agent implements InvocationHandler interface */ public class JdkProxy implements InvocationHandler { private Object targetObject; //Target object requiring proxy //Define the method to get the proxy object (pass in the target object for proxy) public Object getJDKProxy(Object targetObject){ //Assign a value to the target object target this.targetObject = targetObject; //JDK dynamic proxy can only proxy classes that implement interfaces, as can be seen from the parameters required by the newProxyInstance function Object proxyObject = Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),targetObject.getClass().getInterfaces(),this); //Return proxy object return proxyObject; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("JDK Dynamic agent, start listening!"); // Call the invoke method, and result stores the return value of the method Object result = method.invoke(targetObject,args); System.out.println("JDK Dynamic agent, listening is over!"); return result; } // public static void main(String[] args) { // JdkProxy jdkProxy = new JdkProxy(); // Instantiate JDKProxy object // UserManager user = (UserManager) jdkProxy.getJDKProxy(new UserManagerImpl()); // Get proxy object // user.addUser("admin","123456"); // } }
CGLIB agent is adopted for implementation: the asm version package needs to be imported to implement the MethodInterceptor interface
import com.proxy.UserManager; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; /** * Cglib Dynamic proxy: * (You need to import two jar packages, asm-5.0.3.jar and cglib-3.1.jar (optional) */ //Cglib dynamic agent to implement MethodInterceptor interface public class CglibProxy implements MethodInterceptor { private Object target;//Target object requiring proxy //Override intercept method @Override public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("Cglib Dynamic agent, start listening!"); Object result = method.invoke(target,args);//Method execution parameters: target object arr parameter array System.out.println("Cglib Dynamic agent, listening is over!"); return result; } //Defines the method to get the proxy object public UserManager getCglibProxy(Object targetObject) { this.target = targetObject;//Assign a value to the target object target Enhancer enhancer = new Enhancer(); //Set the parent class. Because Cglib generates a subclass for the specified class, you need to specify the parent class enhancer.setSuperclass(targetObject.getClass()); //UserManagerImpl enhancer.setCallback(this);//Set callback Object result = enhancer.create();//Create and return proxy objects return (UserManager) result; } // public static void main(String[] args) { // CglibProxy cglibProxy = new CglibProxy(); // Instantiate the cglibproxy object // UserManager user = cglibProxy.getCglibProxy(new UserManagerImpl());// Get proxy object // user.delUser("admin"); // } }
Client call test and results
import com.proxy.CglibProxy.CglibProxy; import com.proxy.JDKProxy.JdkProxy; public class ClientTest { public static void main(String[] args) { JdkProxy jdkProxy = new JdkProxy(); //Instantiate JDKProxy object UserManager userJdk = (UserManager) jdkProxy.getJDKProxy(new UserManagerImpl()); //Get proxy object userJdk.addUser("admin","123456"); CglibProxy cglibProxy = new CglibProxy(); //Instantiate the CglibProxy object UserManager userCglib = cglibProxy.getCglibProxy(new UserManagerImpl());//Get proxy object userCglib.delUser("admin"); } }
[the external link image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-nutNX6y1-1632377230546)(D: \ note \ note image \ image-20210901105700671.png)]
JDK and CGLIB dynamic agent summary
JDK dynamic agent can only generate agents for classes that implement interfaces, not for classes. It is implemented using Java reflection technology, and the process of generating classes is more efficient.
CGLIB implements a proxy for a class. It mainly generates a subclass of a specified class and covers its methods. It is implemented using the asm bytecode framework. The related execution process is more efficient. The process of generating a class can be compensated by the cache. Because it is inheritance, it is better not to declare this class or method as final
JDK proxy does not need the support of a third-party library. It only needs the JDK environment to proxy. Usage conditions: implement InvocationHandler + generate proxy object using Proxy.newProxyInstance + the proxy object must implement the interface
CGLib must rely on CGLib's class library, but it needs classes to implement any interface proxy. It generates a subclass from the specified class and overrides the methods in it. It is an inherited proxy, but it is recommended to use JDK in the environment of interface programming;