代理模式(Proxy Pattern)
对其他对象提供代理,以控制对这个对象的访问,目的对其他对象进行增强,简而言之,类似AOP,他就是一种代理模式的实现,对你的类进行增强(在你的方法或者类调用之前、后进行增强),代理模式分为两种,静态和动态代理,动态代理常常有两种类库用来实现(CGLIB和JDK).以下先从应用入手,然后阐述原理。
staticproxy
public class Glen implements IPerson { public void findRealPeople() { System.out.println("I want a well-educated、sexy girl"); } } public interface IPerson { void findRealPeople(); } public class GlenFather implements IPerson { Glen glen; GlenFather (Glen glen){ this.glen=glen; } public void findRealPeople() { before(); glen.findRealPeople(); after(); } // strengthen private void after() { System.out.println("begin to find a real girl for glen"); } // strengthen private void before() { System.out.println("find a rich girl for glen and sexy and love my son"); } }
To text
public class Test { public static void main(String[] args) { GlenFather father=new GlenFather(new Glen()); father.findRealPeople(); } }
To explain
The image above illustrate that before 'Glen#findRealPeople' was implemented ,the 'GlenFather#before' was implemented which strengthened method of Glen ,and 'GlenFather#after' was implemented which also strengthened method of Glen! but the problem is that if some others want to findRealPeople,do they need to created a new classes to strengthen their method?no! it is bit foolish,now we to solve the problem with DynamicProxy
Dynamic Proxy
JdkProxy
public class Glen implements IPerson { public void findRealPeople() { System.out.println("Glen:I want a well-educated、sexy girl"); } } public interface IPerson { void findRealPeople(); } public class JdkProxy implements InvocationHandler { IPerson iPerson; public IPerson getInstance(IPerson iPerson){ this.iPerson=iPerson; Class<? extends IPerson> aClass = iPerson.getClass(); // here to use Proxy create return (IPerson) Proxy.newProxyInstance(aClass.getClassLoader(),aClass.getInterfaces(),this); } /** * * @param proxy the classes generated * @param method the method that you want to use * @param args the params that you pass on the method * @return * @throws Throwable */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // have a look here,if you just want to implement certain method you can use name of method to control System.out.println(method.getName()); before(); Object invoke = method.invoke(this.iPerson, args); after(); return invoke; } // strengthen private void before() { System.out.println("find a rich girl for someone else and sexy and love my customers"); } // strengthen private void after() { System.out.println("begin to find a real girl for someone else"); } } public class Justine implements IPerson { public void findRealPeople() { System.out.println("Justine:the girl should taller than me"); } }
To test
public class Test { public static void main(String[] args) { JdkProxy jdkProxy=new JdkProxy(); // the instance that is generated by jdkProxy IPerson glenInstance= jdkProxy.getInstance(new Glen()); glenInstance.findRealPeople(); System.out.println("---------------------------------->"); IPerson justineInstance = jdkProxy.getInstance(new Justine()); justineInstance.findRealPeople(); } }
To explain
We can proxy the classes that implements the same interface with jdkproxy in 'java.lang.reflect'
CGLIB
public class CglibProxy implements MethodInterceptor { public Object getInstance(Class clazz) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(clazz); enhancer.setCallback(this); return enhancer.create(); } // strengthen private void before() { System.out.println("find a rich girl for someone else and sexy and love my customers"); } // strengthen private void after() { System.out.println("begin to find a real girl for someone else"); } /** * * @param o the classes you pass on * @param method the methods you execute * @param objects the params you pass on the methods you execute * @param methodProxy method of proxy * @return * @throws Throwable */ public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { before(); Object result = methodProxy.invokeSuper(o, objects); after(); return result; } } public class Glen { public void findRealPeople() { System.out.println("Glen:I want a well-educated、sexy girl"); } }
To test
public class Test { public static void main(String[] args) { CglibProxy cglibProxy=new CglibProxy(); Glen glen= (Glen) cglibProxy.getInstance(Glen.class); glen.findRealPeople(); } }
To explain
you can find out the different points
- The class of Glen do not has to implements any interfaces in cglib but has to do in jdk
- We should gave class of Glen to cglib but we should give Glen.interfaces to jdk
so in base jdkproxy and cglib have different method to carry out,
- for cglib it extends the class you want to be proxy,
- for jdkproxy it implements class that you want to be proxy,
- that is the reason why you should pass the class to cglib but interfaces to jdkproxy
Theory of Dynamic proxy
Theory of JDK
so i thought whether we can get bytecode of the proxy of class and put it into disk ,then compile the file with '.class' Run!!!
public class Test { public static void main(String[] args) throws Exception { JdkProxy jdkProxy=new JdkProxy(); // the instance that is generated by jdkProxy IPerson glenInstance= jdkProxy.getInstance(new Glen()); glenInstance.findRealPeople(); // we can get bytecode with ProxyGenerator byte[] $Proxy0s = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{IPerson.class}); FileOutputStream fileOutputStream=new FileOutputStream("C:\Users\lizi\Desktop\file\$Proxy0.class"); fileOutputStream.write($Proxy0s); fileOutputStream.close(); } }
This is decompiled classes
import com.growup.design.proxy.dynamicproxy.jdkproxy.IPerson; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; public final class $Proxy0 extends Proxy implements IPerson { private static Method m1; private static Method m3; private static Method m2; private static Method m0; public $Proxy0(InvocationHandler paramInvocationHandler) throws { super(paramInvocationHandler); } public final boolean equals(Object paramObject) throws { try { return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue(); } catch (RuntimeException localRuntimeException) { throw localRuntimeException; } catch (Throwable localThrowable) { } throw new UndeclaredThrowableException(localThrowable); } public final void findRealPeople() throws { try { this.h.invoke(this, m3, null); return; } catch (RuntimeException localRuntimeException) { throw localRuntimeException; } catch (Throwable localThrowable) { } throw new UndeclaredThrowableException(localThrowable); } public final String toString() throws { try { return (String)this.h.invoke(this, m2, null); } catch (RuntimeException localRuntimeException) { throw localRuntimeException; } catch (Throwable localThrowable) { } throw new UndeclaredThrowableException(localThrowable); } public final int hashCode() throws { try { return ((Integer)this.h.invoke(this, m0, null)).intValue(); } catch (RuntimeException localRuntimeException) { throw localRuntimeException; } catch (Throwable localThrowable) { } throw new UndeclaredThrowableException(localThrowable); } static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); m3 = Class.forName("com.growup.design.proxy.dynamicproxy.jdkproxy.IPerson").getMethod("findRealPeople", new Class[0]); m2 = Class.forName("java.lang.Object").getMethod("toString", 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()); } }
To explain
- we implement IPerson so that we have methods of IPerson
- the method named findRealPeople execute h.invoke, h is param of 'InvocationHandler h 'in 'java.lang.reflect.Proxy#newProxyInstance' ,we have pass on the JDKProxy.then method named invoke in JDKProxy
we find out m3 has assigned
in short,when we execute method actually execute method named 'JdkProxy#invoke'
CGLIB of JDK
public class Test { public static void main(String[] args) { System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"C:\Users\lizi\Desktop\file\cglib_proxy_classes"); CglibProxy cglibProxy=new CglibProxy(); Glen glen= (Glen) cglibProxy.getInstance(Glen.class); glen.findRealPeople(); } }
we can find classes decompiled, but them are very long therefor,i do not want to put the codes here,just explain
we open the file find out it extends the Glen and rewrite the method named findRealPeople
SUM UP
advantages:
- To seperate the classes of proxy and real classes,in some extent which reduce the coupling of the system
- To strengthen object classes
disadvantages:
- improve the complexity of system
- may diminish the efficieny of system
Be use in practic:
such as change data source by conditions, in each methods of inserting shoud to change data source according to the time or something else, we can extract common logical code in classes of proxy and just do certain logic code in each classes
tips:
- in framwork of Spring, if your beans did not implement any interfaces than it tend use the CGLIB otherwise use JDK,We can of course make changes through configuration, that is 'proxy-target-class' we can set it to true than framework of spring will default use the CGLIB
- CGLIB more efficient than JDKPROXY because CGLIB don't use reflect in base