Agents can be divided into static agents and dynamic agents, and dynamic agents can be divided into dynamic agents of jvm and dynamic agents of cglib. The underlying layer of AOP like spring framework uses the technology of dynamic agent.
What is agency? In fact, agency is a kind of scene. I want to inquire about each other before a blind date, man, matchmaker and woman. At the beginning, the man can't go directly to the woman to talk about love. He should communicate through a matchmaker. Of course, this is just to say that at the beginning, there is no need for matchmakers after meeting. But you still have to use a matchmaker before you meet. (it doesn't feel very appropriate!)
Generally speaking, agent is a software design pattern (scenario), and its purpose is to achieve code reuse. Specifically, the proxy design pattern is a method to access the proxy object without directly accessing the proxy object.
proxy pattern
Proxy mode is a commonly used java design mode. Its feature is that the proxy class has the same interface with 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.
The difference between static agent and dynamic agent
According to the different time of loading the proxy class, we divide the proxy into static proxy and dynamic proxy. If we determine which class is represented when compiling the code, we can directly use the static proxy; If you are not sure, you can use the dynamic loading mechanism of classes to load the proxied classes during code running, which is dynamic proxy, such as RPC framework and Spring AOP mechanism.
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.
Dynamic proxy: The proxy method created by the proxy class when the program is running is called dynamic proxy.
Static proxy
1. 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.
2. Simple implementation of static proxy
According to the class diagram of agent pattern above, write a simple example of static agent. Let me give a rough example here. If the students in a class want to pay the class fee to the teacher, but they all pass their money to the teacher through the monitor. Here, the monitor is the agent for students to pay class fees,
The monitor is the student's agent.
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 * @author Gonjan */ public interface Person { //Turn in the shift fee void giveMoney(); }
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"); } }
StudentsProxy class, which also implements the Person interface, but also holds a student class object. Since it implements the Pearson interface and holds a student object at the same time, it 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(); } }
Let's test how to use the proxy mode:
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(); } }
Operation results:
Zhang San (the agent) is not directly used to execute the shift fee, but the monitor (the agent) is used to execute it. This is the proxy model.
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(); } }
Operation results:
Zhang San has made progress in his study recently!
Zhang San's shift fee is 50 yuan
You can see that you only need to perform other operations before helping Zhang San pay the 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.
3, Dynamic agent
1. 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. For example, you want to add a processing method before each agent's method:
public void giveMoney() { //Add processing method before calling the proxy method beforeMethod(); stu.giveMoney(); }
- 1
- 2
- 3
- 4
- 5
There is only one giveMoney method here. Write the beforeMethod method method once, but if there are many other methods out of giveMoney, you need to write the beforeMethod method method many times, which is troublesome. Let's see how the following dynamic agent is implemented.
2. Simple implementation of dynamic agent
A Proxy class and an InvocationHandler interface are provided under the java.lang.reflect package of Java. JDK dynamic Proxy classes and dynamic Proxy objects can be generated through this class and interface.
To create a dynamic proxy object, see the following code:
- Create an InvocationHandler object
//Create an InvocationHandler associated with the proxy object InvocationHandler stuHandler = new MyInvocationHandler<Person>(stu);
- 1
- 2
- Use the getProxyClass static method of the Proxy class to generate a dynamic Proxy class stuProxyClass
Class<?> stuProxyClass = Proxy.getProxyClass(Person.class.getClassLoader(), new Class<?>[] {Person.class});
- 1
- Obtain a constructor with InvocationHandler parameter in stuProxyClass
Constructor<?> constructor = PersonProxy.getConstructor(InvocationHandler.class);
- 1
- Create a dynamic instance stuProxy through the constructor
Person stuProxy = (Person) cons.newInstance(stuHandler);
- 1
In this regard, a dynamic Proxy object is created. Of course, the above four steps can be simplified through the newProxyInstances method of the Proxy class:
//Create an InvocationHandler associated with the proxy object InvocationHandler stuHandler = new MyInvocationHandler<Person>(stu); //Create a proxy object stuProxy, and each execution method of the proxy object will replace the invoke method in the execution Invocation Person stuProxy= (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, stuHandler)
I'm sure you'll be wondering how the dynamic agent executes and how to execute the methods of the proxy object through the proxy object. Don't worry. Let's take a simple and complete example of dynamic agent. Or the above example of static agent, the monitor needs to help students pay class fees.
First, define a Person interface:
/** * Create Person interface * @author Gonjan */ public interface Person { //Turn in the shift fee void giveMoney(); }
Create the actual class to be proxied:
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 of 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"); } }
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.
Then execute the corresponding method of the proxy object target in the invoke method. Of course, 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; } }
After completing the above work, we can create a dynamic proxy object. The above briefly introduces how to create a dynamic proxy object. We use a simplified method to create a dynamic proxy object:
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(); } }
We execute this ProxyTest class. First think about it. We created a student Zhang San 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. It is also mentioned above that all methods executing the proxy object will be replaced with the invoke method, that is, Finally, the invoke method in StuInvocationHandler is executed. So it's natural to see the following running results.
Operation results:
The agent executes the giveMoney method
Zhang San's shift fee is 50 yuan
The giveMoney method takes 1001ms
As mentioned above, 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.
4, Principle analysis of dynamic agent
1. Dynamic proxy class created by Java Dynamic Proxy
Above, we created a dynamic Proxy object by using the newProxyInstance method of Proxy class. Looking at the source code of this method, we found that it only encapsulates the steps of creating a dynamic Proxy class (red standard part):
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { Objects.requireNonNull(h); final Class<?>[] intfs = interfaces.clone(); final SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } /* * Look up or generate the designated proxy class. */ Class<?> cl = getProxyClass0(loader, intfs); /* * Invoke its constructor with the designated invocation handler. */ try { if (sm != null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); } final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (!Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { cons.setAccessible(true); return null; } }); } return cons.newInstance(new Object[]{h}); } catch (IllegalAccessException|InstantiationException e) { throw new InternalError(e.toString(), e); } catch (InvocationTargetException e) { Throwable t = e.getCause(); if (t instanceof RuntimeException) { throw (RuntimeException) t; } else { throw new InternalError(t.toString(), t); } } catch (NoSuchMethodException e) { throw new InternalError(e.toString(), e); } }
In fact, what we should pay most attention to is Class
byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", Student.class.getInterfaces()); String path = "G:/javacode/javase/Test/bin/proxy/StuProxy.class"; try(FileOutputStream fos = new FileOutputStream(path)) { fos.write(classFile); fos.flush(); System.out.println("proxy class class File written successfully"); } catch (Exception e) { System.out.println("Write file error"); }
Decompile this class file. Let's see what jdk generates for us:
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; import proxy.Person; public final class $Proxy0 extends Proxy implements Person { private static Method m1; private static Method m2; private static Method m3; private static Method m0; /** *Note that this is the construction method of generating proxy class. The method parameter is InvocationHandler type. Do you understand this *Why does the proxy object always execute the invoke method in the InvocationHandler, and the InvocationHandler holds one *The instance of the proxy object can't help but wonder if it is? Yes, that's what you think. * *super(paramInvocationHandler),Is the constructor that calls the parent class Proxy. *Parent class holding: protected InvocationHandler h; *Proxy Construction method: * protected Proxy(InvocationHandler h) { * Objects.requireNonNull(h); * this.h = h; * } * */ public $Proxy0(InvocationHandler paramInvocationHandler) throws { super(paramInvocationHandler); } //This static block was originally at the end. I brought it to the front for easy description static { try { //Look at what's in the static block here. Did you find the giveMoney method. Remember the name given by giveMoney through reflection m3, and ignore the others first m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); m3 = Class.forName("proxy.Person").getMethod("giveMoney", new Class[0]); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); return; } catch (NoSuchMethodException localNoSuchMethodException) { throw new NoSuchMethodError(localNoSuchMethodException.getMessage()); } catch (ClassNotFoundException localClassNotFoundException) { throw new NoClassDefFoundError(localClassNotFoundException.getMessage()); } } /** * *Here, the giveMoney method of the proxy object is called, which directly calls the invoke method in the InvocationHandler and passes m3 in. *this.h.invoke(this, m3, null);It's simple and clear. *Now, think again, the proxy object holds an InvocationHandler object, and the InvocationHandler object holds an object to be proxied, *Then contact the invoke method in the InvacationHandler. Well, that's it. */ public final void giveMoney() throws { try { this.h.invoke(this, m3, null); return; } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } //Note that toString, hashCode and equals methods are omitted to save space. The principle is the same as the giveMoney method. }
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.
5, Summary
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 dynamically Proxy 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.