Proxy mode definition: Provides a proxy for other objects to control access to this object.
One of the 23 commonly used object-oriented software design patterns. Proxy mode is divided into static agent and dynamic agent.
How to understand the proxy mode ?
The best way to think about abstract problems is to be specific!
For example, we need to record the log before and after execution for a business method, for the purpose of decoupling, we can create another class and define a new business method, which can call the original business method and log processing before and after the call, for example:
CarProxy.class public void move() { System.out.println("日志开始记录...."); new Car().move(); System.out.println("日志记录完成...."); }
There are many applications for proxy mode, such as delayed loading of spring containers, AOP enhancement processing, and so on.
One: Static proxy
Static proxies are created by programmers or tools to generate proxy classes and then compile the proxy classes.
The so-called Static is in the program before the existence of the proxy class of bytecode files, the relationship between the proxy class and the delegate class is determined before running.
In a word, your own handwriting proxy class is static proxy.
Inheritance-based static proxies
Target Object
public class Car { public void move() { System.out.println("1.汽车开始跑步"); System.out.println("2.汽车跑到了终点"); }}
Proxy Object
public class CarProxy extends Car { @Override public void move() { System.out.println("日志开始记录...."); super.move(); System.out.println("日志记录完成...."); }}
Test method
public static void main(String[] args) { Car car = new CarProxy(); car.move(); }
interface-based static proxy
Common interface
public interface Moveable { void move();}
Target Object
public class Car implements Moveable { @Override public void move() { System.out.println("汽车行驶中...."); }}
Proxy Object
public class CarProxy implements Moveable{ private Moveable move; @Override public void move() { if(move==null){ move = new Car(); } System.out.println("开始记录日志:"); move.move(); System.out.println("记录日志结束!"); }}
Test method
public static void main(String[] args) throws Exception { Moveable m =new CarProxy(); m.move(); }
Advantages and disadvantages of static proxies
Advantages :
The business class only needs to focus on the business logic itself, ensuring the reusability of the business class. This is the common advantage of the agent.
Disadvantages :
1) An interface of the proxy object only serves one type of object, if the type of proxy is many, it is bound to proxy for each type of method, static agent is not competent when the program size is slightly larger.
- For example, car class move () method needs to log, if there are cars, trains, bicycles, the Move () method also need to log logs, we have to one by one to generate proxy class, too troublesome.
2) If the interface adds a method, all the proxy classes need to implement this method in addition to implementing the method for all implementation classes. Increases the complexity of code maintenance.
Two: Dynamic agent
Dynamic agents are not concerned with the proxy class at the implementation stage, but in the run phase to specify which object.
The source code of the dynamic proxy class is generated dynamically by the JVM according to the mechanism of reflection during the program running.
In short, dynamic proxies are given to the program to generate proxy classes.
Dynamic Agent for JDK
JDK Dynamic Agent Implementation steps:
- Create the class that is being proxied and the interface that implements it;
- Create a class that implements the interface Invocationhandler, which must implement the Invoke method;
Call the static method of proxy and create a proxy class:
The target method is called through the proxy object.
code example
Common interface
public interface Moveable { String move();}
Target Object
public class Car implements Moveable { @Override public String move() { return "汽车行驶中"; }}
Each proxy object has an associated call handler. When the target method is called on a proxy object, the method call is encoded and assigned to the Invoke method that implements the Invocationhandler interface.
To implement the Invocationhandler interface step:
Defines the method with the parameter constructor, which is the instance object to be proxied, and is used to execute the Method.invoke () method (that is, the execution of the target method)
Implement the Invoke () method of the interface, which is used to enhance the method of the target, such as logging and so on. The return value of the method is the return value of the proxy object execution target method. Specific parameters:
Proxy object generated dynamically by proxy
Examples of method target methods
Parameters of the args target method
public class LogHandler implements InvocationHandler{ private Object target; public LogHandler(Object object){ super(); this.target = object; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //增强处理 Object o = method.invoke(target,args); //增强处理 return o; }}
The proxy object is generated by calling the Proxy.newproxyinstance method, with the following parameters:
- Loader class loader for Target class
- Interfaces interfaces implemented by the target class
- Invocationhandler the implementation object of the call handler
public static void main(String[] args) { Moveable move = (Moveable) Proxy.newProxyInstance(Car.class.getClassLoader(), Car.class.getInterfaces(), new LogHandler(new Car())); System.out.println("代理对象:"+move.getClass().getName()); System.out.println("执行方法:"+move.move());}
Print results
代理对象:com.sun.proxy.$Proxy0执行方法:汽车行驶中
Summarize
The dynamic agent of the JDK can only proxy classes that implement the interface, and classes that do not implement the interface cannot implement dynamic proxies.
Dynamic Agent for Cglib
Referencing Cglib's dependency packages
<dependency> <groupId>cglib</groupId> <artifactId>cglib-nodep</artifactId> <version>2.2</version></dependency>
To make it easier to think about it, I wrote the steps in sequence.
public static void main(String[] args) { Enhancer enhancer = new Enhancer(); //设置父类,被代理类(这里是Car.class) enhancer.setSuperclass(Car.class); //设置回调函数 enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { //增强处理... Object o= proxy.invokeSuper(obj, args);//代理类调用父类的方法 //增强处理... return o; } }); //创建代理类并使用回调(用父类Car去引用) Car car = (Car) enhancer.create(); //执行目标方法 System.out.println(car.move()); }
Method interceptors
After implementing the Intercept method of the Methodinterceptor interface, all the generated proxy methods call this method.
The specific parameters of the Intercept method are
- An instance of the Obj target class
- Method target Methods instance (target method instance obtained through reflection)
- Parameters of the args target method
- Example of proxy class
The return value of the method is the return value of the target method.
Summarize
- The dynamic agent of Cglib is to implement the proxy for the class.
- Produces a subclass of the specified target class that intercepts all calls to the parent class method by means of the method interception technique.
- Because it is implemented by inheritance, the final class cannot be used.
Three: Handwritten JDK dynamic agent source code
From above you can see that the core code of the JDK dynamic agent is the Proxy.newproxyinstance method.
Moveable m = (Moveable)Proxy.newProxyInstance(Car.class,new InvocationHandler());
Implementation principle
- Declare a section of source code, dynamic generation of dynamic agent source
- Source generates Java files and compiles Java files
- Get the class file after compiling the build
- Load the class file into memory and generate a proxy class object to return.
The following is attached to my handwritten source code!
Production Agent object class, Core Class! Difficulties, ideas are here ~
public class Proxy {/** * Production proxy Object * @param clazz * @param h * @return * @throws Exception */ public static Object Newproxyinstance (class Clazz,invocationhandler H) throws exception{//proxy class name String CNA me = Clazz.getname (). substring (Clazz.getname (). LastIndexOf (".") +1) + "$Proxy 0"; Handwriting proxy class source StringBuilder Source = GetSource (Clazz, H, CNAME); Generates the proxy class for the Java file String filename = Thread.CurrentThread (). Getcontextclassloader (). GetResource (""). GetPath () + Clazz.getpackage (). GetName (). replaceall ("\ \", "/") + "/" +cname+ ". Java"; SYSTEM.OUT.PRINTLN (filename); Writes the source code to the file files = new file (filename); Fileutil.writestringtofile (file, source.tostring ()); Compile//get compiler Javacompiler complier = Toolprovider.getsystemjavacompiler (); File Manager Standardjavafilemanager filemgr = Complier.getstandardfilemanager (null, NULL, NULL); Get file Iterable units = filemgr.getjavafileobjects (filename); Compilation task Compilationtask t = complier.gettask (NULL, filemgr, NULL, NULL, NULL, units); Compile the T.call (); Filemgr.close (); Load to memory ClassLoader CL = Classloader.getsystemclassloader (); class<?> C = Cl.loadclass (Clazz.getpackage (). GetName () + "." +cname); Gets the constructor Constructor constructor<?> ctr = C.getconstructor (H.getclass ()); return ctr.newinstance (h); }/** * Handwriting agent class SOURCE * @param clazz * @param h * @param CNAME * @return */private static STRINGB Uilder GetSource (Class clazz, Invocationhandler H, string cname) {//Call processing interface String handler = H.getclass (). GE Tname (); NewLine symbol String line = "\ r \ n"; String space = ""; Proxy class Source StringBuilder Source = new StringBuilder (); Package declaration Source.append ("packages" + Space + clazz.getpackage (). GetName () + ";"). Append (line); Gets the name of the class Source.append (Modifier.tostring (Clazz.getmodifiers ()) + Space + "class" + Space + CNAME + space); Inherit interface Source.append ("implements" + space); Class[] interfaces = Clazz.getinterfaces (); for (int i = 0; i < interfaces.length; i++) {Source.append (Interfaces[i].getname ()); if (i! = interfaces.length-1) {source.append (","); }} source.append ("{"). Append (line); Declare the variable source.append ("private" + handler + "H;"). Append (line); Construction method Source.append ("public" + CNAME + "(" + handler + "h) {"). Append (line); Source.append ("THIS.H=H;"). Append (line), Append ("}"). Append (line); Implement all Methods method[] methods = Clazz.getdeclaredmethods (); For (method Method:methods) {//Get method return type Class returntype = Method.getreturntype (); Gets all comments on the method annotation[] annotations = method.getdeclaredannotations (); for (Annotation annotation:annotations) {//Print note type source.append ("@" + Annotation . Annotationtype (). GetName ()). append (line); }//Print method declaration Source.append (Modifier.tostring (Method.getmodifiers ()) + "" + returntype.getname () + "" + method.getname () + "("); Gets all parameters of the method parameter[] Parameters = Method.getparameters (); Parameter string StringBuilder args = new StringBuilder (); for (int i = 0; i < parameters.length; i++) {//parameter type, formal parameter (all arg123). ) Source.append (Parameters[i].gettype (). GetName () + "" + parameters[i].getname ()); Args.append (Parameters[i].getname ()); if (i! = parameters.length-1) {source.append (","); Args.append (","); }} source.append (") {"). Append (line); Method Logic Source.append ("Object obj=null; \ n try {"). append (line); Source.append ("class[") args = new class[]{"+ args +"}; "). Append (line); Source.append (Method.class.getName () + "Method =" + clazz.getname () + ". Class.getmethod (\" "+ method.getname () +" \ ", args );"). Append (line); Source.append ("obj = H.invoke (This,method,args);"). Append (line); Source.append ("} catch (Exception e) {\ n" + "e.printstacktrace (); \ n" + "}catch (throwable throwable) {\ n" + "Throwable.printstacktrace (); \ n" + "}"). Append (line); Method ends Source.append ("Return (Obj!=null)?" (" +returntype.getname () + ") obj:null;\n}"). Append (line); }//Class End Source.append ("}"); return source; }}
Unit Test
public static void main(String[] args) throws Exception { Moveable m = (Moveable) Proxy.newProxyInstance(Car.class, new TimeHandler(new Car())); String s = m.move(); System.out.println(s); }
Test results
/D:/IDEA/workSpace/myJdkProxy/target/classes/cn/zyzpp/client/Car$Proxy0.java开始记录时间汽车行驶中....耗时:481毫秒!success
I only tested the part, there is a bug is inevitable ~~~!
Project Address: Github