浅谈java代理
【0】README
0.1) 本文描述+源代碼均 轉(zhuǎn)自 core java volume 1, 旨在理解 java代理機制 的基礎(chǔ)知識;
【1】代理相關(guān)
1.1)利用代理可以在運行時創(chuàng)建一個實現(xiàn)了一組給定接口的新類, 這種功能只有在 編譯時無法確定需要實現(xiàn)哪個接口時 才有必要使用;
1.2)假設(shè)有一個表示接口的 Class對象, 它的確切類型在編譯時無法知道。 要想構(gòu)造一個實現(xiàn)這些接口的類, 就需要使用 newInstance 方法或反射找出這個類的構(gòu)造器;但是不能實例化一個接口, 需要在程序處于運行狀態(tài)時定義一個新類;
1.3)解決問題的方法(代理機制): 代理機制是一種更好的解決方法。代理類可以在運行時創(chuàng)建全新的類, 這樣代理類能夠?qū)崿F(xiàn)指定的接口,尤其是, 它具有以下方法:
- 1.3.1)指定接口所需要的全部方法;
- 1.3.2) Object 類中的全部方法, 例如 , toString, equals等;
- 1.3.3)然而,不能在運行時定義這些方法的新代碼。而是要提供一個 調(diào)用處理器(InvocationHandler), 該處理器是實現(xiàn)了 InvocationHandler 接口的類對象。在這個接口中只有一個方法: Object invoke(Object proxy, Method method, Object[] args) ; 要知道, 無論何時調(diào)用代理對象的方法, 調(diào)用處理器的invoke 方法都會被調(diào)用, 并向其傳遞Method對象和原始的調(diào)用參數(shù);
1.4)要想創(chuàng)建一個代理對象, 需要使用Proxy類的 newProxyInstance 方法, 該方法有3個參數(shù):
- 1.4.1)一個類加載器:
- 1.4.2)一個Class 對象數(shù)組: 每個元素都是需要實現(xiàn)的接口;
- 1.4.3)一個調(diào)用處理器:
1.5)但是,還有兩個問題需要解決:
當(dāng)然了,這兩個問題的答案取決于打算使用代理機制解決什么問題。使用代理可能出于很多原因,如:
- ex1)路由對遠程服務(wù)費去的方法調(diào)用;
- ex2)在程序運行期間, 將用戶接口事件與動作關(guān)聯(lián)起來;
- ex3)為調(diào)試, 跟蹤方法調(diào)用;
【2】看個荔枝(跟蹤方法調(diào)用):
public class ProxyTest {public static void main(String[] args){Object[] elements = new Object[1000];// fill elements with proxies for the integers 1 ... 1000for (int i = 0; i < elements.length; i++){Integer value = i + 1;InvocationHandler handler = new TraceHandler(value);Object proxy = Proxy.newProxyInstance(null, new Class[] { Comparable.class } , handler);elements[i] = proxy;}// construct a random integerInteger key = new Random().nextInt(elements.length) + 1;// search for the keyint result = Arrays.binarySearch(elements, key);// print match if foundif (result >= 0) System.out.println(elements[result]);} }class TraceHandler implements InvocationHandler {private Object target;public TraceHandler(Object t){target = t;}public Object invoke(Object proxy, Method m, Object[] args) throws Throwable{// print implicit argumentSystem.out.print(target);// print method nameSystem.out.print("." + m.getName() + "(");// print explicit argumentsif (args != null){for (int i = 0; i < args.length; i++){System.out.print(args[i]);if (i < args.length - 1) System.out.print(", ");}}System.out.println(")");// invoke actual methodreturn m.invoke(target, args);} }2.1)打印結(jié)果:
【2】代理類的特性
2.1)需要記住: 代理類是在程序運行過程中創(chuàng)建的, 然而, 一旦被創(chuàng)建, 就變成了常規(guī)類,與虛擬機中的任何其他類沒有區(qū)別;
2.2)所有的代理類都擴展于 Proxy類。一個代理類只有一個實例域——調(diào)用處理器, 它定義在 Proxy的超類中, 為了履行代理對象的職責(zé), 所需要的任何附加數(shù)據(jù)都必須存儲在調(diào)用處理器中;
2.3)所有的代理類都覆蓋了 Object類中的方法 toString、equals 和 hashCode 方法;這些方法僅僅 調(diào)用了 調(diào)用處理器 的invoke 方法;Object類中的其他方法沒有被重新定義;
2.4)沒有定義代理類的名字的話, Sun 虛擬機中的Proxy類將生成一個 以 字符串 $Proxy 開頭的類名;
2.5)對于特定的類加載器和預(yù)設(shè)的一組接口來說, 只能有一個代理類;也就是說, 如果使用 同一個類加載器和接口數(shù)組調(diào)用兩次 newProxyInstance 方法的話, 那么只能夠得到同一個類的兩個對象, 也可以利用 getProxyClass 方法獲得這個類:
2.6)代理類一定是 public 和 final, 如果代理類實現(xiàn)的所有接口都是 public, 代理類就不屬于某個特定的包;否則,所有非公有的接口都必須屬于同一個包;
2.7)可以調(diào)用 Proxy類中的 isProxyClass 方法 檢測一個特定的 Class對象是否代表一個代理類;
[API] java.lang,reflect.InvocationHandler 1.3
Object invoke(Object proxy, Method method, Object[] args):返回實現(xiàn)指定接口的代理類; static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler handler) : 構(gòu)造一個實現(xiàn)指定接口的代理類的實例, 所有方法都將調(diào)用給定處理器對象的invoke 方法; static boolean isProxyClass(Class c) : 如果 c 是一個代理類返回 true;總結(jié)
- 上一篇: NP-完全性介绍
- 下一篇: 贪婪算法+小应用(调度问题)