目錄
什么是代理?
代理模式
動態代理
Java中常用的代理模式
問題來了,如何動態生成代理類?
動態代理底層實現
什么是代理?
顧名思義,代替某個對象去處理一些問題,謂之代理,那么何為動態?即讓JVM虛擬機去完成而非程序員去完成(與靜態對比),連起來就是讓虛擬機去動態的創建一個對象去代替另一個對象完成某些業務需求;
呢么其中就涉及到了兩個對象,代理類和目標類;代理類又被前輩歸納成代理模式,下面看代理模式;
代理模式
代理模式是GoF23種設計模式之一。屬于結構型設計模式。
代理模式的作用是:為其他對象提供一種代理以控制對這個對象的訪問。在某些情況下,一個客戶不想或者不能直接引用一個對象,此時可以通過一個稱之為“代理”的第三者來實現間接引用。代理對象可以在客戶端和目標對象之間起到中介的作用,并且可以通過代理對象去掉客戶不應該看到的內容和服務或者添加客戶需要的額外服務。 通過引入一個新的對象來實現對真實對象的操作或者將新的對象作為真實對象的一個替身,這種實現機制即為代理模式,通過引入代理對象來間接訪問一個對象,這就是代理模式的模式動機。
代理模式中的角色:
- 代理類(代理主題)
- 目標類(真實主題)
- 代理類和目標類的公共接口(抽象主題):客戶端在使用代理類時就像在使用目標類,不被客戶端所察覺,所以代理類和目標類要有共同的行為,也就是實現共同的接口。
為什么要有個公共接口呢?
呢你代理類要代替目標類完成某些操作,你是不是就需要擁有目標類該有的功能,如何擁有這個類擁有的功能呢?一、代理類繼承目標類(但是繼承太死板,耦合度太高,而且當需求量上去后,你是不是要寫n個子類,不明智!!!)二、通過實現共同接口,實現必要方法,nice!!!,完美解決;
?知道基本概念后,我們完善一下,用更加官方的話來表示。。。
動態代理
在程序運行階段,在內存中動態生成代理類,被稱為動態代理,目的是為了減少代理類的數量。解決代碼復用的問題。
Java中常用的代理模式
- jdk動態代理;
- cglib靜態代理;
- Javassist動態代理技術;
?我們主要分析JDK代理技術
JDK動態代理(理解):使用java反射包中的類和接口實現動態代理的功能,反射包java.lang.reflect,里面有三個類:InvocationHandler,Method,Proxy
?|? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??下面我們通過一個實際業務需求來分析動態代理? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?|
假設某項目已上線,并且運行正常,只是客戶反饋系統有一些地方運行較慢,要求項目組對系統進行優化。于是項目負責人就下達了這個需求。首先需要搞清楚是哪些業務方法耗時較長,于是讓我們統計每個業務方法所耗費的時長。你坑定不能直接在已經上線并且運行很好的項目的源代碼上操作啊,于是乎我們采用靜態代理方法,代碼如下:
// 目標接口
public interface OrderService {void add();void update();void delete();
}
// 目標類
public class OrderServiceImpl implements OrderService {@Overridepublic void add() {System.out.println("添加用戶");}@Overridepublic void update() {System.out.println("修改用戶");}@Overridepublic void delete() {System.out.println("刪除用戶");}
}
// 代理類
public class OrderServiceProxy implements OrderService {@Overridepublic void add() {long begin = System.currentTimeMillis();OrderService.add();long end = System.currentTimeMillis();System.out.println("耗時"+(end - begin)+"毫秒");}@Overridepublic void update() {long begin = System.currentTimeMillis();OrderService.update();long end = System.currentTimeMillis();System.out.println("耗時"+(end - begin)+"毫秒");}@Overridepublic void delete() {long begin = System.currentTimeMillis();OrderService.delete();long end = System.currentTimeMillis();System.out.println("耗時"+(end - begin)+"毫秒");}
}
在上面的靜態代理方法中,代理類和目標類都實現同一個接口,在代理類中維護目標類對象,并完成對目標類對象方法的增強,這種方式雖然遵循開閉原則,但是代理類和目標類至少是“一對一”的綁定關系,如果需要被代理的目標類個數越多,代理類就會越多,會產生大量重復的代碼,也不利于后期的維護。
于是乎我們使用動態代理技術:目前代碼是這樣的,需要我們動態生成一個代理類:
// 目標接口
public interfaceOrderService {void add();void update();void delete();
}
// 目標類
public class OrderServiceImpl implements OrderService {@Overridepublic void add() {System.out.println("添加用戶");}@Overridepublic void update() {System.out.println("修改用戶");}@Overridepublic void delete() {System.out.println("刪除用戶");}
}
問題來了,如何動態生成代理類?
回歸到文章中心題目,JDK動態代理技術
在JDK中,有一個Proxy類,Proxy類是專門完成代理的操作類,可以通過此類為一個或多個接口動態的生成實現類。這個類有一個靜態方法:newProxyInstance()方法。這個方法的目的就是給我們的目標對象返回一個代理對象。
我們可以看到其中newProxyInstance()方法有三個參數,下面分析三個參數:
- 第一個參數:類加載器。在內存中生成了字節碼,要想執行這個字節碼,也是需要先把這個字節碼加載到內存當中的。所以要指定使用哪個類加載器加載。
- 第二個參數:接口類型。代理類和目標類實現相同的接口,所以要通過這個參數告訴JDK動態代理生成的類要實現哪些接口。
- 第三個參數:調用處理器。這是一個JDK動態代理規定的接口,接口全名:java.lang.reflect.InvocationHandler。顯然這是一個回調接口,也就是說調用這個接口中方法的程序已經寫好了,就差這個接口的實現類了。
所以接下來我們要寫一下java.lang.reflect.InvocationHandler接口的實現類,并且實現接口中的方法,代碼如下:
public class TimerInvocationHandler implements InvocationHandler {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {return null;}
}
InvocationHandler接口中有一個方法invoke,這個invoke方法上有三個參數:
- 第一個參數:Object proxy。代理對象。設計這個參數只是為了后期的方便,如果想在invoke方法中使用代理對象的話,盡管通過這個參數來使用。
- 第二個參數:Method method。目標方法。
- 第三個參數:Object[] args。目標方法調用時要傳的參數。
我們將來肯定是要調用“目標方法”的,但要調用目標方法的話,需要“目標對象”的存在,“目標對象”從哪兒來呢?我們可以給TimerInvocationHandler提供一個構造方法,可以通過這個構造方法傳過來“目標對象”,代碼如下:
public class TimerInvocationHandler implements InvocationHandler {// 目標對象private Object target;// 通過構造方法來傳目標對象public TimerInvocationHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 目標執行之前增強。long begin = System.currentTimeMillis();// 調用目標對象的目標方法Object retValue = method.invoke(target, args);// 目標執行之后增強。long end = System.currentTimeMillis();System.out.println("耗時"+(end - begin)+"毫秒");// 一定要記得返回哦。return retValue;}
}
接下來我們開始調用:
// 創建目標對象OrderService target = new OrderServiceImpl();// 創建代理對象OrderService orderServiceProxy = (OrderService) ProxyUtil.newProxyInstance(target);// 調用代理對象的代理方法orderServiceProxy.add();orderServiceProxy.update();orderServiceProxy.delete();
?成功;
動態代理底層實現
現在我們思考一個問題:在使用動態代理之前,我們有一個目標類、一個公共接口;我們如何通過二者和proxy類返回一個代理類對象呢?
我們知道創建一個對象就先要有這個類才能new對象,那現在代理類都還不存在,該怎么去構造代理對象呢?
JDK動態代理的目的就是通過接口來生成代理類以及代理類的對象,我們知道接口是不能直接通過new關鍵字創建對象的。那么JDK動態代理是怎么創建出代理類以及代理類對象的呢?
?