proxy pattern
- Proxy mode is one of 23 design modes, which means that an object a can have the same behavior as B by holding another object B. In order to open the protocol to the outside world, B often implements an interface, and a will also implement the interface. But B is the real implementation class, while a is "virtual". A borrows the method of B to implement the method of the interface. Although a is a "puppet army", it can enhance B and do other things before and after calling B's method. Spring AOP uses dynamic proxy to complete the dynamic implantation of code.
- The benefits of using dynamic agent are more than these. If a project depends on the interface given by another project, but the interface of another project is unstable and the protocol is often changed, an agent can be used. When the interface is changed, only the agent needs to be modified without modifying the business code. In this sense, we can do this for all external interfaces to prevent external code from invading our code. This is called defensive programming.
- In the above example, class A writes and holds B, which is the static proxy of B. If the object of agent A is uncertain, it is A dynamic agent. At present, there are two common implementations of dynamic agent, JDK dynamic agent and CGLIB dynamic agent.
JDK dynamic agent
JDK dynamic agent is a class library provided by JRE, which can be used directly without relying on a third party. Let's take a look at the use code of JDK dynamic agent to understand the principle.
First, there is a star interface. There are two methods: singing and dancing.
package proxy; public interface Star { String sing(String name); String dance(String name); }
Another star implementation class - Andy Lau.
package proxy; public class LiuDeHua implements Star { public String sing(String name) { System.out.println("Give me a glass of loveless water!"); return "Finish singing"; } public String dance(String name) { System.out.println("Dj Lx Remix "); return "Finish jumping"; } }
Before a star performs, someone needs to collect money. Because he has to prepare for the performance, he doesn't do this work himself. He usually gives it to an agent. It is easy to understand. Its name ends with Proxy, but it is not a Proxy class. The reason is that it does not implement our star interface and cannot provide external services. It is just a wrapper.
package proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class StarProxy implements InvocationHandler { //Target class, proxied object private Object target; public void setTarget(Object target) { this.target = target; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //You can enhance it here System.out.println("Collect money"); Object result = method.invoke(target, args); return result; } //Generate proxy class public Object CreateProxyedObj() { return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); } }
In the above example, the object returned by the method createproxyedobj is our proxy class, which requires three parameters. The first two parameters mean to create an object through the interface under the same classloader. The object requires a property, that is, the third parameter, which is an InvocationHandler. It should be noted that this createproxyedobj method does not have to be in our StarProxy class, but often in a factory class. The code usage process of the above agent is generally as follows:
1. new a target object
2. new an InvocationHandler to set the target object
3. Create a proxy object through creatiproxyedobj, which can be forcibly converted to the interface type of the target object. In fact, the generated proxy object implements the target interface.
package proxy; public class Client { public static void main(String[] args) { Star ldh = new LiuDeHua(); StarProxy proxy = new StarProxy(); proxy.setTarget(ldh); Star proxyedObj = (Star) proxy.CreateProxyedObj(); proxyedObj.sing("Lau Andy"); } }
Proxy (provided by jdk Class Library) generates an implementation class according to the interface of B. we call it C. It is a dynamic proxy class (this type is the "new type" of $proxy + number). The generation process is: because you get the interface, you can get all the information of the interface (mainly the definition of methods), and you can declare a new type to implement all the methods of the interface. Obviously, these methods are "virtual" and call the methods of another object. Of course, the called object cannot be object B. if it is object B, we can't enhance it. It's equivalent to sparing a circle and coming back.
Therefore, it calls the wrapper class of B, which needs us to implement, but jdk gives a constraint that it must implement InvocationHandler. In the above example, it is StarProxy. There is a method in this interface, which is the invocation entry of all methods of all targets. We can add our own code enhancement before calling.
Look at our implementation. We called the method of object B (target) in InvocationHandler, and enhanced the method of B before calling.
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // Enhanced here System.out.println("Collect money"); Object result = method.invoke(target, args); return result; }
Therefore, we can think that C acts as an InvocationHandler, and InvocationHandler acts as our class B, two-level agent.
These are the secrets of the whole JDK dynamic agent. In a word, the dynamic agent is to generate a wrapper class object. Because the object of the agent is dynamic, it is called dynamic agent. Because we need to enhance, this enhancement needs to be left to developers to develop code. Therefore, the agent class cannot directly contain the proxied object, but an InvocationHandler. This InvocationHandler contains the proxied object and is responsible for distributing requests to the proxied object. Enhancements can be made before and after distribution. It can be seen from the principle that JDK dynamic agent is the agent of "object".
CGLIB dynamic proxy
The purpose of proxy is to construct an object with the same behavior as the proxy object. The behavior of an object is defined in the class, and the object is only an instance of the class. Therefore, it is not necessary to construct an agent by holding a wrapper object.
Through inheritance, you can inherit all the exposed methods of the parent class, and then you can override these methods. When overriding, you can enhance these methods. This is the idea of CGLIB. According to the principle of Richter substitution (LSP), where the parent class needs to appear, the child class can appear, so the proxy implemented by CGLIB can also be used normally.
package proxy; import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; public class CglibProxy implements MethodInterceptor { // The proxy class is generated according to a type. This method does not need to be placed in the MethodInterceptor public Object CreatProxyedObj(Class<?> clazz) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(clazz); enhancer.setCallback(this); return enhancer.create(); } @Override public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable { // Enhanced here System.out.println("Collect money"); return arg3.invokeSuper(arg0, arg2); } }
It can be seen from the code that it is different from jdk dynamic proxy. From the external performance, creatiproxyedobj only needs one type clazz to generate a proxy object. Therefore, it is said to be a "proxy of class", and the created object is also a new type found by printing type. Unlike jdk dynamic proxy, jdk dynamic proxy requires that the object must implement the interface (the second parameter of three parameters), which is not required by cglib.
The principle of cglib is like this. It generates a type C (proxy class) that inherits B. this proxy class holds a MethodInterceptor, which is passed in when we setCallback. C rewrites all methods in B (the method names are the same), and then in C, build a method named "cglib"+“ father class square method name Parent class method name The method of "parent class method name" (hereinafter called cglib method, all non private methods will be built), and there is only one sentence super in the method body The method name () can be simply considered as maintaining a reference to the parent method for easy calling.
In this way, there are rewriting methods, cglib methods, parent class methods (invisible) and a unified interception method (enhanced method intercept) in C. There must be a mapping relationship between rewriting method and cglib method.
The rewriting method of C is the entrance of external calls (LSP principle). It calls the intercept method of MethodInterceptor. Four parameters will be passed during the call. The first parameter passes this, which represents the proxy class itself. The second parameter indicates the intercepted method, the third parameter is the input parameter, and the fourth parameter is the cglib method. After the enhancement of the intercept method, We call the cglib method and indirectly call the parent method to complete the call of the whole method chain.