別看小弟工做了一段時間,但對代理的理解一直不行,甚至根本就是懵逼狀態。而後今天忽然開竅。就趕忙記下一些理解。html
java 的動態代理主要使用java.lang.reflect.Proxy。若是本身建立一個代理類的話,就須要自定義一個class實現invocationHandler,並重寫invoke方法才行,這樣自定義的class 才能在invoke中建立代理並調用被代理的方法。java
而invocationHandler 接口只有一個invoke方法。就是給代理對象處理業務用的。spring
下面在說下Proxy,該對象主要負責建立代理,建立代理對象的源碼以下:數組
@CallerSensitive 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); } }
三個參數分別是:緩存
一、代理對象的類加載器,經過調用getParent 發現是sun.misc.Launcher$ExtClassLoader@41629346框架
證實是APPClassLoader,系統級的類加載器jvm
二、是一個class對象接口數組,該數組都是多態形式的,你能夠傳實現類的實體對象,但該方法返回的必定是對應多態的引用對象ide
三、代理類對象相關聯的invocationHandler,是invocationHandler對象測試
好了。瞭解完參數。該怎麼建立一個對象的動態代理對象呢?ui
步驟以下:
一、建立一個實體接口
package com.wisely.proxy; /** * DES:代理對象實體bean * Created by Reynole-白 * Date: 2017/8/26 16:27 */ public interface Person { void sing(String songName); void dance(String danceName); }
二、建立其實現
package com.wisely.proxy; /** * DES: * Created by Reynole-白 * Date: 2017/8/26 16:29 */ public class LiuDeHua implements Person { public LiuDeHua(){ System.out.println("我是華仔的無參構造器"); } @Override public void sing(String songName) { System.out.println("華仔唱:" + songName); } @Override public void dance(String danceName) { System.out.println("華仔跳:" + danceName); } }
三、建立與動態代理類相關的invocationHandler類,invoke的三個參數的定義在方法註釋中有體現。
package com.wisely.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; /** * DES:代理對象 要牛逼的 * Created by Reynole-白 * Date: 2017/8/26 16:30 */ public class LiuDeHuaProxy implements InvocationHandler { private Person pp = new LiuDeHua(); /** * 代理對象執行的invoke方法,若是想讓代理對象作些邏輯操做,能夠在invoke中進行編碼 * @param proxy 表示代理對象,這個對象纔是真正的動態代理對象 * @param method 代理對象當前執行的方法 * @param args 方法參數 * @return * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String methodName = method.getName(); Object obj = args[0]; if(obj instanceof String){ System.out.println("傳入的參數是string類型的"); } //代理對象要作的事情 if("sing".equals(methodName)){ System.out.println("我是華仔的代理對象,找他唱歌先給錢~!!!" + args[0]); System.out.println("已收到你的10K RMB,通知華仔……"); pp.sing(args[0].toString()); }else if("dance".equals(methodName)){ System.out.println("我是華仔的代理對象,找他跳舞,先經過我這關!!~~" + args[0]); System.out.println("你已通關,通知華仔……"); pp.dance(args[0].toString()); } return null; } }
測試類:若是不是多態形式接收代理生成的Object 那麼編譯不經過
package com.wisely.proxy; import java.lang.reflect.Proxy; /** * DES: 代理測試類 * Created by Reynole-白 * Date: 2017/8/26 16:38 */ public class ProxyTestMain { public static void main(String[] args) { Person p = new LiuDeHua();//被代理的對象 Person p2 = new JavaFatcher(); /** * 這裏代理對象的生成其實能夠寫到代理對象中,以匿名內部類的形式 * 這裏要注意,代理對象 必需要以多態的形式定義 */ ClassLoader cl = LiuDeHuaProxy.class.getClassLoader(); System.out.println(cl.getParent()); Person personProxy = (Person) Proxy.newProxyInstance(LiuDeHuaProxy.class.getClassLoader(),p.getClass().getInterfaces(), new LiuDeHuaProxy()); personProxy.sing("冰雨"); personProxy.dance("迪斯科"); } }
運行結果以下:
我是華仔的無參構造器
sun.misc.Launcher$ExtClassLoader@41629346
我是華仔的無參構造器
傳入的參數是string類型的
我是華仔的代理對象,找他唱歌先給錢~!!!冰雨
已收到你的10K RMB,通知華仔……
華仔唱:冰雨
傳入的參數是string類型的
我是華仔的代理對象,找他跳舞,先經過我這關!!~~迪斯科
你已通關,通知華仔……
華仔跳:迪斯科
Process finished with exit code 0
-------------------------------------------------------------------------------------------------
以上例子,參考了http://blog.csdn.net/pangqiandou/article/details/52964066 這篇博客的例子
其使用場景 就是在大多數框架設計以及 封裝設計的時候使用。通常搬磚的像我這樣的猿尚未接觸到。但若是想看spring這類框架的源碼的話尤爲是AOP ,是須要了解其動態代理的。
以上,有不對的地方,請個位大大海涵,並友情指正。拜謝!!!
下一步好好研究一下反射。
=============================================================
補充。由於小弟近期在看dubbo的源碼。接觸到了dubbo 的代理模式。就複習了jdk 的接口類型的動態代理。
感受以前理解的東西有些出入。特此補充一下:
一、實現InvocationHandler的類 是一箇中介類。。負責關聯動態生成的代理,並調用被代理對象的方法。
二、代理類在日誌輸出上都有一些特殊的標記,如:
$Proxy0
0表明第幾個。多個會依次累加。
三、真正建立動態代理對象的是Proxy 的newProxyInstance方法 中的Class<?> cl = getProxyClass0(loader, intfs);這句。
四、動態代理對象 在建立時,是存放在jvm緩存中的class文件。反編譯後爲:
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; /** *注意這裏是生成代理類的構造方法,方法參數爲InvocationHandler類型,看到這,是否是就有點明白 *爲什麼代理對象調用方法都是執行InvocationHandler中的invoke方法,而InvocationHandler又持有一個 *被代理對象的實例,不由會想難道是....? 沒錯,就是你想的那樣。 * *super(paramInvocationHandler),是調用父類Proxy的構造方法。 *父類持有:protected InvocationHandler h; *Proxy構造方法: * protected Proxy(InvocationHandler h) { * Objects.requireNonNull(h); * this.h = h; * } * */ public $Proxy0(InvocationHandler paramInvocationHandler) throws { super(paramInvocationHandler); } //這個靜態塊原本是在最後的,我把它拿到前面來,方便描述 static { try { //看看這兒靜態塊兒裏面有什麼,是否是找到了giveMoney方法。請記住giveMoney經過反射獲得的名字m3,其餘的先無論 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()); } } /** * *這裏調用代理對象的giveMoney方法,直接就調用了InvocationHandler中的invoke方法,並把m3傳了進去。 *this.h.invoke(this, m3, null);這裏簡單,明瞭。 *來,再想一想,代理對象持有一個InvocationHandler對象,InvocationHandler對象持有一個被代理的對象, *再聯繫到InvacationHandler中的invoke方法。嗯,就是這樣。 */ 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); } } //注意,這裏爲了節省篇幅,省去了toString,hashCode、equals方法的內容。原理和giveMoney方法一毛同樣。 }
仔細觀察 反編譯的 動態代理class 就會明白。爲什麼 實現invocationHandler 類中invoke 方法的三個參數哪裏來的對象。怎麼用的。
以上補充 參考自:https://www.cnblogs.com/gonjan-blog/p/6685611.html