詳解java動態代理機制以及使用場景

詳解java動態代理機制以及使用場景
https:
//blog.csdn.net/u011784767/article/details/78281384

深刻理解java動態代理的實現機制

https://blog.51cto.com/4247649/2109130

 (1)什麼是代理? 大道理上講代理是一種軟件設計模式,目的地但願能作到代碼重用。具體上講,代理這種設計模式是經過不直接訪問被代理對象的方式,而訪問被代理對象的方法。這個就比如 商戶---->明星經紀人(代理)---->明星這種模式。咱們能夠不經過直接與明星對話的狀況下,而經過明星經紀人(代理)與其產生間接對話。 (2)什麼狀況下使用代理? (1)設計模式中有一個設計原則是開閉原則,是說對修改關閉對擴展開放,咱們在工做中有時會接手不少前人的代碼,裏面代碼邏輯讓人摸不着頭腦(sometimes the code is really like shit),這時就很難去下手修改代碼,那麼這時咱們就能夠經過代理對類進行加強。 (2)咱們在使用RPC框架的時候,框架自己並不能提早知道各個業務方要調用哪些接口的哪些方法 。那麼這個時候,就可用經過動態代理的方式來創建一箇中間人給客戶端使用,也方便框架進行搭建邏輯,某種程度上也是客戶端代碼和框架鬆耦合的一種表現。 (3)Spring的AOP機制就是採用動態代理的機制來實現切面編程。 (3)靜態代理和動態代理 咱們根據加載被代理類的時機不一樣,將代理分爲靜態代理和動態代理。若是咱們在代碼編譯時就肯定了被代理的類是哪個,那麼就能夠直接使用靜態代理;若是不能肯定,那麼可使用類的動態加載機制,在代碼運行期間加載被代理的類這就是動態代理,好比RPC框架和Spring AOP機制。 (4)靜態代理 咱們先建立一個接口,遺憾的是java api代理機制求被代理類必需要實現某個接口,對於靜態代理方式代理類也要實現和被代理類相同的接口;對於動態代理代理類則不須要顯示的實現被代理類所實現的接口。 /** * 頂層接口 * @author yujie.wang * */
public interface Person { public void sayHello(String content, int age); public void sayGoodBye(boolean seeAgin, double time); } /** * 須要被代理的類 實現了一個接口Person * @author yujie.wang * */
public class Student implements Person{ @Override public void sayHello(String content, int age) { // TODO Auto-generated method stub
        System.out.println("student say hello" + content + " "+ age); } @Override public void sayGoodBye(boolean seeAgin, double time) { // TODO Auto-generated method stub
        System.out.println("student sayGoodBye " + time + " "+ seeAgin); } } /** * 靜態代理,這個代理類也必需要實現和被代理類相同的Person接口 * @author yujie.wang * */
public class ProxyTest implements Person{ private Person o; public ProxyTest(Person o){ this.o = o; } public static void main(String[] args) { // TODO Auto-generated method stub //s爲被代理的對象,某些狀況下 咱們不但願修改已有的代碼,咱們採用代理來間接訪問
        Student s = new Student(); //建立代理類對象
        ProxyTest proxy = new ProxyTest(s); //調用代理類對象的方法
        proxy.sayHello("welcome to java", 20); System.out.println("******"); //調用代理類對象的方法
        proxy.sayGoodBye(true, 100); } @Override public void sayHello(String content, int age) { // TODO Auto-generated method stub
        System.out.println("ProxyTest sayHello begin"); //在代理類的方法中 間接訪問被代理對象的方法
 o.sayHello(content, age); System.out.println("ProxyTest sayHello end"); } @Override public void sayGoodBye(boolean seeAgin, double time) { // TODO Auto-generated method stub
        System.out.println("ProxyTest sayHello begin"); //在代理類的方法中 間接訪問被代理對象的方法
 o.sayGoodBye(seeAgin, time); System.out.println("ProxyTest sayHello end"); } } 測試代碼輸出: ProxyTest sayHello begin student say hellowelcome to java 20 ProxyTest sayHello end ****** ProxyTest sayHello begin student sayGoodBye 100.0 true ProxyTest sayHello end 靜態代理看起來是比較簡單的,沒有什麼問題只不過是在代理類中引入了被代理類的對象而已。 那麼接下來咱們看看動態代理。 (5)動態代理 咱們先直接上動態代理的代碼,以後再分析代碼的行爲,上面的Person接口和Student被代理類保持不變。 /** * 動態代理,動態代理類不要顯示的實現被代理類所實現的接口 * @author yujie.wang * */
public class MyInvocationHandler implements InvocationHandler{ private Object object; public MyInvocationHandler(Object object){ this.object = object; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // TODO Auto-generated method stub
        System.out.println("MyInvocationHandler invoke begin"); System.out.println("proxy: "+ proxy.getClass().getName()); System.out.println("method: "+ method.getName()); for(Object o : args){ System.out.println("arg: "+ o); } //經過反射調用 被代理類的方法
 method.invoke(object, args); System.out.println("MyInvocationHandler invoke end"); return null; } public static void main(String [] args){ //建立須要被代理的類
        Student s = new Student(); //這一句是生成代理類的class文件,前提是你須要在工程根目錄下建立com/sun/proxy目錄,否則會報找不到路徑的io異常
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true"); //得到加載被代理類的 類加載器
        ClassLoader loader = Thread.currentThread().getContextClassLoader(); //指明被代理類實現的接口
        Class<?>[] interfaces = s.getClass().getInterfaces(); // 建立被代理類的委託類,以後想要調用被代理類的方法時,都會委託給這個類的invoke(Object proxy, Method method, Object[] args)方法
        MyInvocationHandler h = new MyInvocationHandler(s); //生成代理類
        Person proxy = (Person)Proxy.newProxyInstance(loader, interfaces, h); //經過代理類調用 被代理類的方法
        proxy.sayHello("yujie.wang", 20); proxy.sayGoodBye(true, 100); System.out.println("end"); } } 運行測試代碼輸出以下結果: MyInvocationHandler invoke begin proxy: com.sun.proxy.$Proxy0 method: sayHello arg: yujie.wang arg: 20 student say helloyujie.wang 20 MyInvocationHandler invoke end MyInvocationHandler invoke begin proxy: com.sun.proxy.$Proxy0 method: sayGoodBye arg: true arg: 100.0 student sayGoodBye 100.0 true MyInvocationHandler invoke end end 仔細分析上面的動態代理實現代碼,咱們看到這裏涉及到java反射包下的一個接口InvocationHandler和一個類Proxy。 package java.lang.reflect; public interface InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; } 這個接口只有一個invoke方法,咱們在經過代理類調用被代理類的方法時,最終都會委託給這個invoke方法執行, //經過代理類調用 被代理類的方法
        proxy.sayHello("yujie.wang", 20); proxy.sayGoodBye(true, 100); 因此咱們就能夠在這個invoke方法中對被代理類進行加強或作一些其餘操做。 Proxy類的public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)方法內部經過拼接字節碼的方式來建立代理類,後面我會反編譯出它所建立的代理類看看內容。 咱們看這個方法的三個參數: ClassLoader loader:指定一個動態加載代理類的類加載器 Class<?>[] interfaces:指明被代理類實現的接口,以後咱們經過拼接字節碼生成的類才能知道調用哪些方法。 InvocationHandler h:這是一個方法委託類,咱們經過代理調用被代理類的方法時,就能夠將方法名和方法參數都委託給這個委託類。 你看咱們如今有了類加載器、類實現的接口、要調用方法的方法名和參數,那麼咱們就能夠作不少事情了。 (6)反編譯Proxy.newProxyInstance所建立的代理類 //這一句是生成代理類的class文件,前提是你須要在工程根目錄下建立com/sun/proxy目錄,否則會報找不到路徑的io異常
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true"); 咱們在代碼中加入上述代碼,代碼就會保存生成的代理類,名稱爲$Proxy0.class 經過jd-gui反編譯代碼以下,其中註釋是我加上去的: package com.sun.proxy; import com.yujie.proxy.dynamic.Person; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; /** *代理類也實現了Person接口,看起來和靜態代理的方式也會同樣的 *同時代理類也繼承了Proxy類 */ 
public final class $Proxy0 extends Proxy implements Person{ private static Method m4; private static Method m1; private static Method m0; private static Method m3; private static Method m2; public $Proxy0(InvocationHandler paramInvocationHandler) throws { super(paramInvocationHandler); } //實現了Person接口的方法,這就是咱們調用這個方法Proxy.newProxyInstance必須提供第二個參數的做用 
  public final void sayGoodBye(boolean paramBoolean, double paramDouble) throws { try { // 咱們看到經過調用代理類的方法時,最終方法都會委託給InvocationHandler實現類的invoke方法 // m4爲代理類經過反射得到的Method
      this.h.invoke(this, m4, new Object[] { Boolean.valueOf(paramBoolean), Double.valueOf(paramDouble) }); return; } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final boolean equals(Object paramObject) throws { try { return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue(); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final int hashCode() throws { try { return ((Integer)this.h.invoke(this, m0, null)).intValue(); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } //實現了Person接口的方法,這就是咱們調用這個方法Proxy.newProxyInstance必須提供第二個參數的做用 
  public final void sayHello(String paramString, int paramInt) throws { try { // 咱們看到經過調用代理類的方法時,最終方法都會委託給InvocationHandler實現類的invoke方法 // m4爲代理類經過反射得到的Method
      this.h.invoke(this, m3, new Object[] { paramString, Integer.valueOf(paramInt) }); return; } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final String toString() throws { try { return (String)this.h.invoke(this, m2, null); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } static { try {//代理類經過反射 得到的接口方法Method
      m4 = Class.forName("com.yujie.proxy.dynamic.Person").getMethod("sayGoodBye", new Class[] { Boolean.TYPE, Double.TYPE }); m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); //代理類經過反射 得到的接口方法Method
      m3 = Class.forName("com.yujie.proxy.dynamic.Person").getMethod("sayHello", new Class[] { Class.forName("java.lang.String"), Integer.TYPE }); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); return; } catch (NoSuchMethodException localNoSuchMethodException) { throw new NoSuchMethodError(localNoSuchMethodException.getMessage()); } catch (ClassNotFoundException localClassNotFoundException) { throw new NoClassDefFoundError(localClassNotFoundException.getMessage()); } } } 總結一下: jdk的代理讓咱們在不直接訪問某些對象的狀況下,經過代理機制也能夠訪問被代理對象的方法,這種技術能夠應用在不少地方好比RPC框架,Spring AOP機制,可是咱們看到jdk的代理機制必需要求被代理類實現某個方法,這樣在生成代理類的時候才能知道從新那些方法。這樣一個沒有實現任何接口的類就沒法經過jdk的代理機制進行代理,固然解決方法是使用cglib的代理機制進行代理。
相關文章
相關標籤/搜索