利用代理可以在運行時創建一個實現了一組接口的新類。
這種功能只有在編譯時無法確定需要實現哪個接口時才使用。對於應用程序設計人員來說,遇到這種情況的機會很少。這是一種高級技術,對於系統程序設計人員來說,代理帶來的靈活性十分重要。
代理如何工作的
代理類可以在運行時創建全新的類。在運行時直接生成類,即Class類的一個實例。而非生成一個類的源碼,然後經過編譯後再生成類的字節碼文件,再經過JVM加載、生成類。因此運行時生成類性能很快。
代理類能夠實現指定的接口的所有需要的方法,並且也實現了Object類的toString、equals方法。
代理類並非是通過自己全新的定義來實現這些接口方法的,而是通過一個調用處理器即InvocationHandler接口的一個實例對象來實現的。
InvocationHandler
接口中只有一個方法:
/*
@param poxy 代理類
@param method 當代理類工作時,調用被代理對象的那個方法;即代理類實現的接口方法。
@param args 被代理的方法的參數
*/
Object invoke(Object proxy, Method method, Object[] args)
代理類的代理作用即代理要做的工作就要寫在這個invoke方法中,再method.invoke(args);方法前後做一些必要的工作。
無論何時調用代理對象的方法,invoke方法都會被調用,invoke方法中必須給出處理調用的代碼。
創建代理對象
需要用JDK的Proxy類的newProxyInstance方法:
static Object newProxyInstance(ClassLoader loader, Class<?> interfaces, InvocationHandler h);
在運行時動態生成類,首先要指定類加載器 loader,然後指明對哪些接口——interfaces生成代理類,最後確定生成的代理類最終是調用哪個被代理類的哪些方法——invocationHandler實例——一個調用處理器。
使用代理類的可能場景
- 路由對遠程服務的方法調用
- 在程序運行期間,將用戶接口事件與動作關聯起來
- 爲了調試,跟蹤方法調用
舉個例子
追蹤Arrays.binarySearch方法的查找過程:輸入查找一個給定的key時,經過了幾次比較,每次是同哪個數比較的。
分析:binarySearch方法是JDK的方法,它調用comparable接口的compareTo方法來做比較和查找,我們無法在這個方法裏插入代碼,來做輸出,但是代理可以做到。
代理就是在執行本要做的事情的前後,再做一些其它的事情,來實現代理的功能。
import java.lang.reflect.*;
import java.util.*;
class ComparableProxy implements InvocationHandler
{
private Object target;
public IntegerProxy(Object target){
this.target = target;
}
public Object invoke(Object p,Method m,Object [] args) throws Throwable{
// 在做比較工作前,輸入比較對象信息
StringBuilder sb = new StringBuilder(this.toString()).append("],[target:").append(target.toString()).append("],[args:").append(Arrays.toString(args));
System.out.println(sb.toString());
// 調用實際工作的比較方法
return m.invoke(target,args);
}
}
import static java.lang.System.out;
import java.util.*;
import java.lang.reflect.*;
public class Test{
public static void main(String[] args){
Object [] proxys = new Object[1000];
for(int i=0;i<1000;i++){
proxys[i] = Proxy.newProxyInstance(null, new Class[]{Comparable.class}, new ComparableProxy(i+1));
}
int key = new Random().nextInt(1000) + 1;
out.println("key:"+key);
int result = Arrays.binarySearch(proxys, key);
out.println("result:"+result);
}
}
/*
key:780
[target:500],[args:[780]
[target:750],[args:[780]
[target:875],[args:[780]
[target:812],[args:[780]
[target:781],[args:[780]
[target:765],[args:[780]
[target:773],[args:[780]
[target:777],[args:[780]
[target:779],[args:[780]
[target:780],[args:[780]
result:779
*/
代理類是在程序運行過程中創建的。 一旦被創建, 就變成了常規類, 與虛擬機中的任何其他類沒有什麼區別。
所有的代理類都擴展於 Proxy 類。一個代理類只有一個實例域—調用處理器,它定義超類 Proxy 中。 爲了履行代理對象的職責, 所需要的任何附加數據都必須存儲在調用處理器中。 例如, 在上例中, 代理 Comparable 對象時, ComparableProxy 包裝了實際的對象。