用動態代理的時候,對它新生成的類長什么樣子感到好奇.有幸通過一些資料消除了心里的疑惑.
平時工作使用的Spring框架里面有一個AOP(面向切面)的機制,只知道它是把類重新生成了一遍,在切面上加上了后來定義的邏輯.這樣就達到了動態的在原有類上增加一些功能.比如日志打印,攔截信息等.
這里只關心動態代理技術生成新的類,先不管虛擬機是如何去生成類,用了什么字節碼生成技術,怎么產生字節碼等這一系列動作.現在只關心最后生成的新類長什么樣,它和老類有什么區別.為了獲取到生成后的代理類的字節碼並且反編譯成我們能夠看得懂的代碼,需要實現一個動態代理例子.
例子
//接口
package note.com; /** * Girl接口 * @author lxz * */ public interface IGirl { void sayHello(); }
//接口實現,也是需要利用動態代理擴展功能的類
package note.com; /** * 具體Girl * @author lxz * */ public class MyGirl implements IGirl { public void sayHello() { System.out.println("如花似玉石榴姐"); } }
//代理實現類
package note.com; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * 代理類 * 功能:給IGirl實現類增加介紹 * @author lxz * */ public class ProxyGirl implements InvocationHandler { Object originalObj; Object bind(Object originalObj) { this.originalObj = originalObj; return Proxy.newProxyInstance(originalObj.getClass() .getClassLoader(), originalObj.getClass().getInterfaces(), this); } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("第一美女是:"); return method.invoke(originalObj, args); } }
//測試類
package note.com; /** * 測試類 * * @author lxz * */ public class Test { public static void main(String[] args) { IGirl hello = (IGirl) new ProxyGirl().bind(new MyGirl()); hello.sayHello();
System.out.println(hello.getClass().getName()); } }
結果:
第一美女是:
如花似玉石榴姐
com.sun.proxy.$Proxy0
這里可見hello真實類型是$Proxy0,到底它長什么樣子,往下看.
代理類字節碼反編譯結果
package note.com; 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 IGirl { private static Method m3; private static Method m1; private static Method m0; private static Method m2; /* * 構造函數傳入能夠訪問真實對象的代理類,這個實際是上例Test中的new ProxyGirl() */ protected $Proxy0(InvocationHandler h) { super(h); } /* * 代理實現sayHello, */ public void sayHello() { try { this.h.invoke(this, m3, null); } catch (RuntimeException localRuntimeException) { throw localRuntimeException; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } /* * 代理實現繼承自Object的equals */ public void equals() { try { this.h.invoke(this, m1, null); } catch (RuntimeException localRuntimeException) { throw localRuntimeException; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } /* * 代理實現繼承自Object的hashCode */ public int hashCode() { try { return (Integer) this.h.invoke(this, m0, null); } catch (RuntimeException localRuntimeException) { throw localRuntimeException; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } /* * 代理實現繼承自Object的toString */ public String toString() { try { return (String) this.h.invoke(this, m2, null); } catch (RuntimeException localRuntimeException) { throw localRuntimeException; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } /* * 初始化真實對象中的所有方法 */ static { try { m3 = Class.forName("note.com.IGirl").getMethod("sayHello", new Class[0]); m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); m0 = Class.forName("java.lang.Object").getMethod("equals", new Class[0]); m2 = Class.forName("java.lang.Object").getMethod("equals", new Class[0]); } catch (NoSuchMethodException localNoSuchMethodException) { throw new NoSuchMethodError(localNoSuchMethodException.getMessage()); } catch (ClassNotFoundException localClassNotFoundException) { throw new NoClassDefFoundError( localClassNotFoundException.getMessage()); } } }
通過觀察反編譯后的動態類,這個邏輯並不復雜,主要功能是對所有的方法進行初始化,到執行某個方法的時候調用我們自己實現的代理類去執行擴展功能和原始類的方法.
對原始類和動態代理后產生的類進行比較:
1,$Proxy0訪問真實的類對象通過InvocationHandler的實現類調用.
2,動態代理擴展功能並沒有在$Proxy0中加入,而是回調InvocationHandler的接口,通過子類實現Invoke方法擴展.
從調用關系上看使用動態代理前后:
左邊:是原始的調用關系,原始類中有什么邏輯就執行什么.
右邊:是動態代理以后,通過動態代理生成類的對象調用代理類,代理類調用擴展邏輯,然后調用原始類對象的邏輯.由此實現了對原始類的動態擴展.
通過這樣追本溯源的去了解,我對動態代理的理解更加深刻,也打消了心里的一個疑惑.
ps:
動態代理什么時候用?可以參考這個:動態代理技術實現設計模式-代理模式
文中的字節碼反編譯是參考<<深入理解Java虛擬機 JVM高級特性與最佳實踐>>這本書.