代理模式屬於行爲型模式的一種, 控制對其餘對象的訪問, 起到中介做用.java
代理模式核心角色: 真實角色,代理角色;app
按實現方式不一樣分爲靜態代理和動態代理兩種;框架
控制對其它對象的訪問。ide
JDK自帶了Proxy的實現, 下面咱們先使用JDK的API來演示代理如何使用, 隨後再探究Proxy的實現原理,並本身來實現Proxy.學習
InvocationHandler
,Proxy
)使用JDK實現的代理代碼以下, 先定義業務接口`Car`,而後實現該接口`QQCar`,實現類即爲真實角色. 繼續定義代理類`Agent`,代理類須要實現接口`InvocationHandler`的`invoke()`方法, 最後是調用; 注意代理類使用了`Proxy.newProxyInstance()`方法動態生成代理對象, 在稍後手寫代碼時咱們將參考本方法定義咱們本身的實現方式.
/** * 汽車接口,定義業務規範 */ public interface Car { void sale(); }
/** * 接口Car的具體實現,是代理模式中的真實角色 */ public class QQCar implements Car { public void sale() { System.out.println("賣了一輛qq"); } }
/** * 經紀公司,或者經銷商 */ public class Agent implements InvocationHandler { private Car car ; /** * 返回代理對象,接收被代理對象 * @param car * @return * @throws Exception */ public Object getInstance(Car car) throws Exception { this.car=car; Class clazz = car.getClass(); // 看下代理先後的具體類型 System.out.println("代理前對象的類型"+car.getClass().getName()); Object obj = Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this); // 看下代理先後的具體類型 System.out.println("代理後對象類型變爲"+obj.getClass().getName()); return obj; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Agent find some costumers"); //this.car.sale(); method.invoke(this.car, args); System.out.println("Agent saled the car"); return null; } }
try { Car car = (Car) new Agent().getInstance(new QQCar()); car.sale(); }catch (Exception e){ e.printStackTrace(); }
總結JDK原理以下:this
要本身實現JDK的代理模式,咱們首先要搞清楚JDK的Proxy是如何實現的, 經過調試及查看源碼能夠知道JDK生成了一個$Proxy0
的類型,咱們也將該類型名稱打印到了控制檯. 若是能動態生成,編譯並將這個類加載到內存, 咱們就能夠本身實現Proxy了.spa
$Proxy0
的代碼,供咱們後面生成代理類的源碼時參考//生產接口Car對應的代理類class文件並保存到文件 byte[] data = ProxyGenerator.generateProxyClass("$Proxy0",new Class[]{Car.class}); FileOutputStream outputStream = new FileOutputStream("E:\\design-patterns\\src\\main\\java\\com\\xlx\\pattern\\proxy\\jdk\\$Proxy0.class"); outputStream.write(data); outputStream.close();
生成的$Proxy0.class文件反編譯後的代碼以下, 能夠看到其實現了Car接口.代理
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // import com.xlx.pattern.proxy.jdk.Car; 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 Car { private static Method m1; private static Method m2; private static Method m3; private static Method m0; public $Proxy0(InvocationHandler var1) throws { super(var1); } public final boolean equals(Object var1) throws { try { return (Boolean)super.h.invoke(this, m1, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } public final String toString() throws { try { return (String)super.h.invoke(this, m2, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final void sale() throws { try { super.h.invoke(this, m3, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final int hashCode() throws { try { return (Integer)super.h.invoke(this, m0, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m2 = Class.forName("java.lang.Object").getMethod("toString"); m3 = Class.forName("com.xlx.pattern.proxy.jdk.Car").getMethod("sale"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } }
MyClassLoader
,MyInvocationHandler
,MyProxy
,分別對應ClassLoader
,InvocationHandler
,Proxy
; 其中接口MyInvocationHandler
代碼以下:/** * 代理類須要實現該接口 */ public interface MyInvocationHandler { Object invoke(Object proxy, Method method, Object[] args) throws Throwable; }
MyAgent
對應咱們使用JDK時定義的Agent
, 但getInstance()
方法實現所有改成2中自定義的類,實現2中定義的接口/** * 代理類 */ public class MyAgent implements MyInvocationHandler { private Car car; public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Agent find some costumers"); //this.car.sale(); method.invoke(this.car,args); System.out.println("Agent saled the car"); return null; } public Object getInstance(Car car) throws Exception { this.car=car; Class clazz = car.getClass(); Object obj = MyProxy.newProxyInstance(new MyClassLoader(),clazz.getInterfaces(),this); return obj; } }
實現MyProxy
中生成代理對象的方法newProxyInstance()
具體又分爲如下幾步:調試
1. 定義動態代理類的源碼 2. 保存源碼文件到磁盤 3. 編譯源碼文件爲.class文件 4. 加載.class字節碼到內存 (具體實現見5.實現MyClassLoader)) 5. 返回代理對象
/** * 生成代理對象的代碼, Proxy的具體原理在這裏體現 */ public class MyProxy { private static final String ln = "\r\n"; public static Object newProxyInstance(MyClassLoader loader, Class<?>[] interfaces, MyInvocationHandler h) { File f = null; try { // 第一步: 生成源代碼 String src = generateSrc(interfaces[0]); // 第二步: 保存生成的源碼文件 String filePath = MyProxy.class.getResource("").getPath(); f = new File(filePath + "/$Proxy0.java"); FileWriter writer = new FileWriter(f); writer.write(src); writer.flush(); writer.close(); // 第三步: 編譯生成.class文件 JavaCompiler compliler = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager manager = compliler.getStandardFileManager(null, null, null); Iterable iterable = manager.getJavaFileObjects(f); JavaCompiler.CompilationTask task = compliler.getTask(null, manager, null, null, null, iterable); ((JavaCompiler.CompilationTask) task).call(); manager.close(); // 第四步: 加載class字節碼到內存(MyClassLoader類實現) Class proxyClass = loader.findClass("$Proxy0"); // 第五步: 返回代理對象 Constructor constructor = proxyClass.getConstructor(MyInvocationHandler.class); return constructor.newInstance(h); } catch (Exception e) { e.printStackTrace(); } finally { if (null != f) { f.delete(); } } return null; } /** * 生成源碼的方法 * * @param interfaces 爲了演示,按一個接口處理 * @return */ private static String generateSrc(Class<?> interfaces) { StringBuffer src = new StringBuffer(); src.append("package com.xlx.pattern.proxy.my;" + ln); src.append("import java.lang.reflect.Method;" + ln); src.append("public class $Proxy0 extends MyProxy implements " + interfaces.getName() + "{" + ln); src.append("MyInvocationHandler h;" + ln); src.append("public $Proxy0(MyInvocationHandler h){" + ln); src.append("this.h=h;" + ln); src.append("}" + ln); // 循環定義方法,與被代理類的方法同名 for (Method m : interfaces.getMethods()) { src.append("public " + m.getReturnType().getName() + " " + m.getName() + "(){" + ln); src.append("try{" + ln); src.append("Method m =" + interfaces.getName() + ".class.getMethod(\"" + m.getName() + "\",new Class[]{});" + ln); src.append("this.h.invoke(this,m,null);" + ln); src.append("}catch(Throwable e){e.printStackTrace();}" + ln); src.append("}" + ln); } src.append("}" + ln); return src.toString(); } }
MyClassLoader
的findClass()
方法,最終由父類ClassLoader.defineClass()
方法加載,完成最後拼圖/** * 代碼生成,編譯,從新加載到內存 * 類加載器, 使用ClassLoader */ public class MyClassLoader extends ClassLoader{ File basePath ; public MyClassLoader(){ String basePath = MyClassLoader.class.getResource("").getPath(); this.basePath = new File(basePath) ; } @Override public Class<?> findClass(String name) throws ClassNotFoundException{ String className = MyClassLoader.class.getPackage().getName()+"."+name; if (null!=basePath){ File classFile = new File(basePath,name.replaceAll("\\.","/")+".class"); if (classFile.exists()){ FileInputStream in = null; ByteArrayOutputStream out= null; try { in = new FileInputStream(classFile); out = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len; while ((len=in.read(buffer))!=-1){ out.write(buffer,0,len); } return defineClass(className,out.toByteArray(),0,out.size()); }catch (Exception e){ e.printStackTrace(); }finally { classFile.delete(); if (null!=in){ try{ in.close(); }catch (Exception e){ e.printStackTrace(); } } if (null!=out){ try{ out.close(); }catch (Exception e){ e.printStackTrace(); } } } } } return null; } }
上面我仿照JDK自帶API用本身的代碼實現了Proxy, 這樣寫了一次以後對Proxy的實現原理加深了不少.Proxy做爲一種重要的模式已經大量用在了目前流行的不少框架上, 理解了原理就更有信心去學習框架以及框架的實現思想了.code
優勢: 1. 職責清晰,真實角色專一實現業務邏輯,代理角色去完成具體的事務,代碼結構簡潔清晰;2. 可擴展
上面研究了JDK動態代理的實現, 首先定義了接口,而後用一個類實現這個接口,這個實現類就是要代理的具體對象;
cglib庫也實現了Proxy模式,與JDK不一樣的是, cglib不須要定義接口, 而是經過生成被代理類的子類來實現代理模式.這使得代理模式的使用更加簡單. 通常類型都可以做爲被代理類型.
大體的實現以下(原理跟jdk實現差很少,只不過cglib使用的是類繼承實現):
/** * 演示 cglib 代理方式 */ public class CGAgent implements MethodInterceptor { public Object getInstance(Class clazz) throws Exception{ Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(clazz); enhancer.setCallback(this); return enhancer.create(); } public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("代理開始了...."); methodProxy.invokeSuper(o,objects); System.out.println("代理結束了...."); return null; } }