這一次徹底搞懂JDK動態代理
動態代理 V.S 靜態代理
Proxy類的代碼被固定下來,不會因為業務的逐漸龐大而龐大
可以實現AOP編程,這是靜態代理無法實現的
解耦,如果用在web業務下,可以實現數據層和業務層的分離
動態代理的優勢就是實現無侵入式的代碼擴展。
靜態代理這個模式本身有個大問題,若類方法數量越來越多的時候,代理類的代碼量十分龐大的。所以引入動態代理
動態代理
Java中動態代理的實現的關鍵:
- Proxy
- InvocationHandler
InvocationHandler#invoke
- method 調用的方法,即需要執行的方法
- args 方法的參數
- proxy 代理類的實例 圖片
JDK動態代理
JDK動態代理模式里有個攔截器,在JDK中,只要實現了InvocationHandler接口的類就是一個攔截器類。攔截器的作用:控制目標對象的目標方法的執行。
攔截器的具體操作步驟:
1.引入類
目標類和一些擴展方法相關的類
2.賦值
調用構造器,給相關對象賦值
3.合并邏輯處理
在invoke方法中把所有的邏輯結合在一起。最終決定目標方法是否被調用
示例
思考如下問題:
代理對象由誰產生
JVM,不像靜態代理,我們得自己new個代理對象。
代理對象實現了什么接口
實現的接口是目標對象實現的接口。同靜態代理中代理對象實現的接口。那個繼承關系圖還是相同的。代理對象和目標對象都實現一個共同的接口。就是這個接口。所以Proxy.newProxyInstance()方法返回的類型就是這個接口類型。
代理對象的方法體是什么
代理對象的方法體中的內容就是攔截器中invoke方法中的內容。
所有代理對象的處理邏輯,控制是否執行目標對象的目標方法。都是在這個方法里面處理的。
攔截器中的invoke方法中的method參數是在什么時候賦值的
在客戶端,代理對象調用目標方法的時候,此實例中為:
- proxyObj.business();
實際上進入的是攔截器中的invoke方法,這時攔截器中的invoke方法中的method參數會被賦值。
為啥這叫JDK動態代理
因為該動態代理對象是用JDK相關代碼生成。
很多同學對動態代理一直很迷糊,在于理解錯了
- proxyObj.business();
- $Proxy0
沒有發現這個 proxyObj 和 Proxy 類之間的聯系,一直好奇
最后調用的business()是怎么和 invoke() 聯系上的?
invoke又怎么知道business的存在?
因為大多同學不知道 $Proxy0 類,看看下面的 $Proxy0 源碼,相信你完全可以理解動態代理了。
我們雖然沒有顯式調用invoke,但該方法確實被執行了。
可以從newProxyInstance方法作為突破口,我們先來看一下Proxy類中newProxyInstance方法的源代碼:
- public static Object newProxyInstance(ClassLoader loader,
- Class<?>[] interfaces,
- InvocationHandler h) {
- final Class<?>[] intfs = interfaces.clone();
- final SecurityManager sm = System.getSecurityManager();
- if (sm != null) {
- checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
- }
- /*
- * 查找或生成指定的代理類
- * 創建代理類$Proxy0
- * $Proxy0類實現了interfaces的接口,并繼承了Proxy類
- */
- Class<?> cl = getProxyClass0(loader, intfs);
- /*
- * 使用指定的調用處理程序調用其構造器
- */
- try {
- if (sm != null) {
- checkNewProxyPermission(Reflection.getCallerClass(), cl);
- }
- // 形參為InvocationHandler類型的構造器
- 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});
- } ...
- }
Proxy.newProxyInstance 做了什么呢?
- 根據參數loader和interfaces調用方法 getProxyClass(loader, interfaces)創建代理$Proxy0類。$Proxy0類 實現了interfaces的接口,并繼承了Proxy類
- 實例化$Proxy0,并在構造器把DynamicSubject傳過去,接著$Proxy00調用父類Proxy的構造器,為h賦值
$Proxy0的源碼:
- package com.sun.proxy;
- public final class $Proxy0 extends Proxy implements TargetInterface {
- private static Method m1;
- private static Method m3;
- private static Method m2;
- 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});
- }...
- }
- public final void business() throws {
- try {
- super.h.invoke(this, m3, (Object[])null);
- }...
- }
- public final String toString() throws {
- try {
- return (String)super.h.invoke(this, m2, (Object[])null);
- }...
- }
- public final int hashCode() throws {
- try {
- return (Integer)super.h.invoke(this, m0, (Object[])null);
- }...
- }
- static {
- try {
- m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
- m3 = Class.forName("com.javaedge.design.pattern.structural.proxy.dynamicproxy.jdkdynamicproxy.TargetInterface").getMethod("business");
- m2 = Class.forName("java.lang.Object").getMethod("toString");
- m0 = Class.forName("java.lang.Object").getMethod("hashCode");
- }...
- }
- }
接著把得到的$Proxy0實例強轉成TargetInterface,并將引用賦給TargetInterface。當執行proxyObj.business(),就調用了$Proxy0類中的business()方法,進而調用父類Proxy中的h的invoke()方法。即InvocationHandler.invoke()。
最后提醒Proxy#getProxyClass返回的是Proxy的Class類,而非很同學想當然認為的“被代理類的Class類”!
本文轉載自微信公眾號「JavaEdge」,可以通過以下二維碼關注。轉載本文請聯系JavaEdge公眾號。