国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

Java動(dòng)態(tài)代理機(jī)制綜合分析及擴(kuò)展

開發(fā) 后端
本文通過分析Java動(dòng)態(tài)代理的機(jī)制和特點(diǎn),解讀動(dòng)態(tài)代理類的源代碼,并且模擬推演了動(dòng)態(tài)代理類的可能實(shí)現(xiàn),向讀者闡述了一個(gè)完整的Java動(dòng)態(tài)代理運(yùn)作過程,希望能幫助讀者加深對(duì)Java動(dòng)態(tài)代理的理解和應(yīng)用。

Java動(dòng)態(tài)代理機(jī)制的出現(xiàn),使得Java開發(fā)人員不用手工編寫代理類,只要簡(jiǎn)單地指定一組接口及委托類對(duì)象,便能動(dòng)態(tài)地獲得代理類,這是一套非常靈活有彈性的代理框架。

這里,51CTO編輯向您推薦:Java開發(fā)中代理技術(shù)的使用方法  Java編程中使用動(dòng)態(tài)代理實(shí)現(xiàn)AOP功能

代理:設(shè)計(jì)模式

代理是一種常用的設(shè)計(jì)模式,其目的就是為其他對(duì)象提供一個(gè)代理以控制對(duì)某個(gè)對(duì)象的訪問。代理類負(fù)責(zé)為委托類預(yù)處理消息,過濾消息并轉(zhuǎn)發(fā)消息,以及進(jìn)行消息被委托類執(zhí)行后的后續(xù)處理。

為了保持行為的一致性,代理類和委托類通常會(huì)實(shí)現(xiàn)相同的接口,所以在訪問者看來兩者沒有絲毫的區(qū)別。通過代理類這中間一層,能有效控制對(duì)委托類對(duì)象的直接訪問,也可以很好地隱藏和保護(hù)委托類對(duì)象,同時(shí)也為實(shí)施不同控制策略預(yù)留了空間,從而在設(shè)計(jì)上獲得了更大的靈活性。Java動(dòng)態(tài)代理機(jī)制以巧妙的方式近乎完美地實(shí)踐了代理模式的設(shè)計(jì)理念。

相關(guān)的類和接口

要了解Java動(dòng)態(tài)代理的機(jī)制,首先需要了解以下相關(guān)的類或接口:java.lang.reflect.Proxy:這是Java動(dòng)態(tài)代理機(jī)制的主類,它提供了一組靜態(tài)方法來為一組接口動(dòng)態(tài)地生成代理類及其對(duì)象。

  1. 清單1.Proxy的靜態(tài)方法  
  2. //方法1:該方法用于獲取指定代理對(duì)象所關(guān)聯(lián)的調(diào)用處理器  
  3. staticInvocationHandlergetInvocationHandler(Objectproxy)  
  4. //方法2:該方法用于獲取關(guān)聯(lián)于指定類裝載器和一組接口的動(dòng)態(tài)代理類的類對(duì)象  
  5. staticClassgetProxyClass(ClassLoaderloader,Class[]interfaces)  
  6. //方法3:該方法用于判斷指定類對(duì)象是否是一個(gè)動(dòng)態(tài)代理類  
  7. staticbooleanisProxyClass(Classcl)  
  8. //方法4:該方法用于為指定類裝載器、一組接口及調(diào)用處理器生成動(dòng)態(tài)代理類實(shí)例  
  9. staticObjectnewProxyInstance(ClassLoaderloader,Class[]interfaces,  
  10. InvocationHandlerh) 

java.lang.reflect.InvocationHandler:這是調(diào)用處理器接口,它自定義了一個(gè)invoke方法,用于集中處理在動(dòng)態(tài)代理類對(duì)象上的方法調(diào)用,通常在該方法中實(shí)現(xiàn)對(duì)委托類的代理訪問。

  1. 清單2.InvocationHandler的核心方法  
  2. //該方法負(fù)責(zé)集中處理動(dòng)態(tài)代理類上的所有方法調(diào)用。第一個(gè)參數(shù)既是代理類實(shí)例,第二個(gè)參數(shù)是被調(diào)用的方法對(duì)象  
  3. //第三個(gè)方法是調(diào)用參數(shù)。調(diào)用處理器根據(jù)這三個(gè)參數(shù)進(jìn)行預(yù)處理或分派到委托類實(shí)例上發(fā)射執(zhí)行  
  4. Objectinvoke(Objectproxy,Methodmethod,Object[]args) 

每次生成動(dòng)態(tài)代理類對(duì)象時(shí)都需要指定一個(gè)實(shí)現(xiàn)了該接口的調(diào)用處理器對(duì)象(參見Proxy靜態(tài)方法4的第三個(gè)參數(shù))。java.lang.ClassLoader:這是類裝載器類,負(fù)責(zé)將類的字節(jié)碼裝載到Java虛擬機(jī)(JVM)中并為其定義類對(duì)象,然后該類才能被使用。Proxy靜態(tài)方法生成動(dòng)態(tài)代理類同樣需要通過類裝載器來進(jìn)行裝載才能使用,它與普通類的唯一區(qū)別就是其字節(jié)碼是由JVM在運(yùn)行時(shí)動(dòng)態(tài)生成的而非預(yù)存在于任何一個(gè).class文件中。
每次生成動(dòng)態(tài)代理類對(duì)象時(shí)都需要指定一個(gè)類裝載器對(duì)象(參見Proxy靜態(tài)方法4的第一個(gè)參數(shù))

代理機(jī)制及其特點(diǎn)

首先讓我們來了解一下如何使用Java動(dòng)態(tài)代理。具體有如下四步驟:

1.通過實(shí)現(xiàn)InvocationHandler接口創(chuàng)建自己的調(diào)用處理器;

2.通過為Proxy類指定ClassLoader對(duì)象和一組interface來創(chuàng)建動(dòng)態(tài)代理類;

3.通過反射機(jī)制獲得動(dòng)態(tài)代理類的構(gòu)造函數(shù),其唯一參數(shù)類型是調(diào)用處理器接口類型;

4.通過構(gòu)造函數(shù)創(chuàng)建動(dòng)態(tài)代理類實(shí)例,構(gòu)造時(shí)調(diào)用處理器對(duì)象作為參數(shù)被傳入。

  1. 清單3.動(dòng)態(tài)代理對(duì)象創(chuàng)建過程  
  2. //InvocationHandlerImpl實(shí)現(xiàn)了InvocationHandler接口,并能實(shí)現(xiàn)方法調(diào)用從代理類到委托類的分派轉(zhuǎn)發(fā)  
  3. //其內(nèi)部通常包含指向委托類實(shí)例的引用,用于真正執(zhí)行分派轉(zhuǎn)發(fā)過來的方法調(diào)用  
  4. InvocationHandlerhandler=newInvocationHandlerImpl(..);  
  5. //通過Proxy為包括Interface接口在內(nèi)的一組接口動(dòng)態(tài)創(chuàng)建代理類的類對(duì)象  
  6. Classclazz=Proxy.getProxyClass(classLoader,newClass[]{Interface.class,...});  
  7. //通過反射從生成的類對(duì)象獲得構(gòu)造函數(shù)對(duì)象  
  8. Constructorconstructor=clazz.getConstructor(newClass[]{InvocationHandler.class});  
  9. //通過構(gòu)造函數(shù)對(duì)象創(chuàng)建動(dòng)態(tài)代理類實(shí)例  
  10. InterfaceProxy=(Interface)constructor.newInstance(newObject[]{handler}); 

實(shí)際使用過程更加簡(jiǎn)單,因?yàn)镻roxy的靜態(tài)方法newProxyInstance已經(jīng)為我們封裝了步驟2到步驟4的過程,所以簡(jiǎn)化后的過程如下:

  1. 清單4.簡(jiǎn)化的動(dòng)態(tài)代理對(duì)象創(chuàng)建過程  
  2. //InvocationHandlerImpl實(shí)現(xiàn)了InvocationHandler接口,并能實(shí)現(xiàn)方法調(diào)用從代理類到委托類的分派轉(zhuǎn)發(fā)  
  3. InvocationHandlerhandler=newInvocationHandlerImpl(..);  
  4. //通過Proxy直接創(chuàng)建動(dòng)態(tài)代理類實(shí)例  
  5. Interfaceproxy=(Interface)Proxy.newProxyInstance(classLoader,  
  6. newClass[]{Interface.class},  
  7. handler); 

#p#
接下來讓我們來了解一下Java動(dòng)態(tài)代理機(jī)制的一些特點(diǎn)。首先是動(dòng)態(tài)生成的代理類本身的一些特點(diǎn)。

1)包:如果所代理的接口都是public的,那么它將被定義在頂層包(即包路徑為空),如果所代理的接口中有非public的接口(因?yàn)榻涌诓荒鼙欢x為protect或private,所以除public之外就是默認(rèn)的package訪問級(jí)別),那么它將被定義在該接口所在包(假設(shè)代理了com.ibm.developerworks包中的某非public接口A,那么新生成的代理類所在的包就是com.ibm.developerworks),這樣設(shè)計(jì)的目的是為了最大程度的保證動(dòng)態(tài)代理類不會(huì)因?yàn)榘芾淼膯栴}而無法被成功定義并訪問;

2)類修飾符:該代理類具有final和public修飾符,意味著它可以被所有的類訪問,但是不能被再度繼承;

3)類名:格式是“$ProxyN”,其中N是一個(gè)逐一遞增的阿拉伯?dāng)?shù)字,代表Proxy類第N次生成的動(dòng)態(tài)代理類,值得注意的一點(diǎn)是,并不是每次調(diào)用Proxy的靜態(tài)方法創(chuàng)建動(dòng)態(tài)代理類都會(huì)使得N值增加,原因是如果對(duì)同一組接口(包括接口排列的順序相同)試圖重復(fù)創(chuàng)建動(dòng)態(tài)代理類,它會(huì)很聰明地返回先前已經(jīng)創(chuàng)建好的代理類的類對(duì)象,而不會(huì)再嘗試去創(chuàng)建一個(gè)全新的代理類,這樣可以節(jié)省不必要的代碼重復(fù)生成,提高了代理類的創(chuàng)建效率。

4)類繼承關(guān)系:該類的繼承關(guān)系如圖:

由圖可見,Proxy類是它的父類,這個(gè)規(guī)則適用于所有由Proxy創(chuàng)建的動(dòng)態(tài)代理類。而且該類還實(shí)現(xiàn)了其所代理的一組接口,這就是為什么它能夠被安全地類型轉(zhuǎn)換到其所代理的某接口的根本原因。

接下來讓我們了解一下代理類實(shí)例的一些特點(diǎn)。每個(gè)實(shí)例都會(huì)關(guān)聯(lián)一個(gè)調(diào)用處理器對(duì)象,可以通過Proxy提供的靜態(tài)方法getInvocationHandler去獲得代理類實(shí)例的調(diào)用處理器對(duì)象。

在代理類實(shí)例上調(diào)用其代理的接口中所聲明的方法時(shí),這些方法最終都會(huì)由調(diào)用處理器的invoke方法執(zhí)行,此外,值得注意的是,代理類的根類java.lang.Object中有三個(gè)方法也同樣會(huì)被分派到調(diào)用處理器的invoke方法執(zhí)行,它們是hashCode,equals和toString,可能的原因有:一是因?yàn)檫@些方法為public且非final類型,能夠被代理類覆蓋;二是因?yàn)檫@些方法往往呈現(xiàn)出一個(gè)類的某種特征屬性,具有一定的區(qū)分度,所以為了保證代理類與委托類對(duì)外的一致性,這三個(gè)方法也應(yīng)該被分派到委托類執(zhí)行。

當(dāng)代理的一組接口有重復(fù)聲明的方法且該方法被調(diào)用時(shí),代理類總是從排在最前面的接口中獲取方法對(duì)象并分派給調(diào)用處理器,而無論代理類實(shí)例是否正在以該接口(或繼承于該接口的某子接口)的形式被外部引用,因?yàn)樵诖眍悆?nèi)部無法區(qū)分其當(dāng)前的被引用類型。

接著來了解一下被代理的一組接口有哪些特點(diǎn)。首先,要注意不能有重復(fù)的接口,以避免動(dòng)態(tài)代理類代碼生成時(shí)的編譯錯(cuò)誤。其次,這些接口對(duì)于類裝載器必須可見,否則類裝載器將無法鏈接它們,將會(huì)導(dǎo)致類定義失敗。再次,需被代理的所有非public的接口必須在同一個(gè)包中,否則代理類生成也會(huì)失敗。最后,接口的數(shù)目不能超過65535,這是JVM設(shè)定的限制。

最后再來了解一下異常處理方面的特點(diǎn)。從調(diào)用處理器接口聲明的方法中可以看到理論上它能夠拋出任何類型的異常,因?yàn)樗械漠惓6祭^承于Throwable接口,但事實(shí)是否如此呢?答案是否定的,原因是我們必須遵守一個(gè)繼承原則:即子類覆蓋父類或?qū)崿F(xiàn)父接口的方法時(shí),拋出的異常必須在原方法支持的異常列表之內(nèi)。所以雖然調(diào)用處理器理論上講能夠,但實(shí)際上往往受限制,除非父接口中的方法支持拋Throwable異常。

那么如果在invoke方法中的確產(chǎn)生了接口方法聲明中不支持的異常,那將如何呢?放心,Java動(dòng)態(tài)代理類已經(jīng)為我們?cè)O(shè)計(jì)好了解決方法:它將會(huì)拋出UndeclaredThrowableException異常。這個(gè)異常是一個(gè)RuntimeException類型,所以不會(huì)引起編譯錯(cuò)誤。通過該異常的getCause方法,還可以獲得原來那個(gè)不受支持的異常對(duì)象,以便于錯(cuò)誤診斷。

代碼是最好的老師

機(jī)制和特點(diǎn)都介紹過了,接下來讓我們通過源代碼來了解一下Proxy到底是如何實(shí)現(xiàn)的。首先記住Proxy的幾個(gè)重要的靜態(tài)變量:

  1. 清單5.Proxy的重要靜態(tài)變量  
  2. //映射表:用于維護(hù)類裝載器對(duì)象到其對(duì)應(yīng)的代理類緩存  
  3. privatestaticMaploaderToCache=newWeakHashMap();  
  4. //標(biāo)記:用于標(biāo)記一個(gè)動(dòng)態(tài)代理類正在被創(chuàng)建中  
  5. privatestaticObjectpendingGenerationMarker=newObject();  
  6. //同步表:記錄已經(jīng)被創(chuàng)建的動(dòng)態(tài)代理類類型,主要被方法isProxyClass進(jìn)行相關(guān)的判斷  
  7. privatestaticMapproxyClasses=Collections.synchronizedMap(newWeakHashMap());  
  8. //關(guān)聯(lián)的調(diào)用處理器引用  
  9. protectedInvocationHandlerh; 

然后,來看一下Proxy的構(gòu)造方法:

  1. 清單6.Proxy構(gòu)造方法  
  2. //由于Proxy內(nèi)部從不直接調(diào)用構(gòu)造函數(shù),所以private類型意味著禁止任何調(diào)用  
  3. privateProxy(){}  
  4. //由于Proxy內(nèi)部從不直接調(diào)用構(gòu)造函數(shù),所以protected意味著只有子類可以調(diào)用  
  5. protectedProxy(InvocationHandlerh){this.h=h;} 

接著,可以快速瀏覽一下newProxyInstance方法,因?yàn)槠湎喈?dāng)簡(jiǎn)單:

  1. 清單7.Proxy靜態(tài)方法newProxyInstance  
  2. publicstaticObjectnewProxyInstance(ClassLoaderloader,  
  3. Class<?>[]interfaces,  
  4. InvocationHandlerh)  
  5. throwsIllegalArgumentException{  
  6.  
  7. //檢查h不為空,否則拋異常  
  8. if(h==null){  
  9. thrownewNullPointerException();  
  10. }  
  11.  
  12. //獲得與制定類裝載器和一組接口相關(guān)的代理類類型對(duì)象  
  13. Classcl=getProxyClass(loader,interfaces);  
  14.  
  15. //通過反射獲取構(gòu)造函數(shù)對(duì)象并生成代理類實(shí)例  
  16. try{  
  17. Constructorcons=cl.getConstructor(constructorParams);  
  18. return(Object)cons.newInstance(newObject[]{h});  
  19. }catch(NoSuchMethodExceptione){thrownewInternalError(e.toString());  
  20. }catch(IllegalAccessExceptione){thrownewInternalError(e.toString());  
  21. }catch(InstantiationExceptione){thrownewInternalError(e.toString());  
  22. }catch(InvocationTargetExceptione){thrownewInternalError(e.toString());  
  23. }  

由此可見,動(dòng)態(tài)代理真正的關(guān)鍵是在getProxyClass方法,該方法負(fù)責(zé)為一組接口動(dòng)態(tài)地生成代理類類型對(duì)象。在該方法內(nèi)部,您將能看到Proxy內(nèi)的各路英雄(靜態(tài)變量)悉數(shù)登場(chǎng)。有點(diǎn)迫不及待了么?那就讓我們一起走進(jìn)Proxy最最神秘的殿堂去欣賞一番吧。該方法總共可以分為四個(gè)步驟:

對(duì)這組接口進(jìn)行一定程度的安全檢查,包括檢查接口類對(duì)象是否對(duì)類裝載器可見并且與類裝載器所能識(shí)別的接口類對(duì)象是完全相同的,還會(huì)檢查確保是interface類型而不是class類型。這個(gè)步驟通過一個(gè)循環(huán)來完成,檢查通過后將會(huì)得到一個(gè)包含所有接口名稱的字符串?dāng)?shù)組,記為String[]interfaceNames。總體上這部分實(shí)現(xiàn)比較直觀,所以略去大部分代碼,僅保留留如何判斷某類或接口是否對(duì)特定類裝載器可見的相關(guān)代碼。

  1. 清單8.通過Class.forName方法判接口的可見性  
  2. try{  
  3. //指定接口名字、類裝載器對(duì)象,同時(shí)制定initializeBoolean為false表示無須初始化類  
  4. //如果方法返回正常這表示可見,否則會(huì)拋出ClassNotFoundException異常表示不可見  
  5. interfaceClass=Class.forName(interfaceName,false,loader);  
  6. }catch(ClassNotFoundExceptione){  

#p#
從loaderToCache映射表中獲取以類裝載器對(duì)象為關(guān)鍵字所對(duì)應(yīng)的緩存表,如果不存在就創(chuàng)建一個(gè)新的緩存表并更新到loaderToCache。緩存表是一個(gè)HashMap實(shí)例,正常情況下它將存放鍵值對(duì)(接口名字列表,動(dòng)態(tài)生成的代理類的類對(duì)象引用)。當(dāng)代理類正在被創(chuàng)建時(shí)它會(huì)臨時(shí)保存(接口名字列表,pendingGenerationMarker)。標(biāo)記pendingGenerationMarke的作用是通知后續(xù)的同類請(qǐng)求(接口數(shù)組相同且組內(nèi)接口排列順序也相同)代理類正在被創(chuàng)建,請(qǐng)保持等待直至創(chuàng)建完成。

  1. 清單9.緩存表的使用  
  2. do{  
  3. //以接口名字列表作為關(guān)鍵字獲得對(duì)應(yīng)cache值  
  4. Objectvalue=cache.get(key);  
  5. if(valueinstanceofReference){  
  6. proxyClass=(Class)((Reference)value).get();  
  7. }  
  8. if(proxyClass!=null){  
  9. //如果已經(jīng)創(chuàng)建,直接返回  
  10. returnproxyClass;  
  11. }elseif(value==pendingGenerationMarker){  
  12. //代理類正在被創(chuàng)建,保持等待  
  13. try{  
  14. cache.wait();  
  15. }catch(InterruptedExceptione){  
  16. }  
  17. //等待被喚醒,繼續(xù)循環(huán)并通過二次檢查以確保創(chuàng)建完成,否則重新等待  
  18. continue;  
  19. }else{  
  20. //標(biāo)記代理類正在被創(chuàng)建  
  21. cache.put(key,pendingGenerationMarker);  
  22. //break跳出循環(huán)已進(jìn)入創(chuàng)建過程  
  23. break;  
  24. }while(true); 

動(dòng)態(tài)創(chuàng)建代理類的類對(duì)象。首先是確定代理類所在的包,其原則如前所述,如果都為public接口,則包名為空字符串表示頂層包;如果所有非public接口都在同一個(gè)包,則包名與這些接口的包名相同;如果有多個(gè)非public接口且不同包,則拋異常終止代理類的生成。確定了包后,就開始生成代理類的類名,同樣如前所述按格式“$ProxyN”生成。類名也確定了,接下來就是見證奇跡的發(fā)生——動(dòng)態(tài)生成代理類:

  1. 清單10.動(dòng)態(tài)生成代理類  
  2. //動(dòng)態(tài)地生成代理類的字節(jié)碼數(shù)組  
  3. byte[]proxyClassFile=ProxyGenerator.generateProxyClass(proxyName,interfaces);  
  4. try{  
  5. //動(dòng)態(tài)地定義新生成的代理類  
  6. proxyClass=defineClass0(loader,proxyName,proxyClassFile,0,  
  7. proxyClassFile.length);  
  8. }catch(ClassFormatErrore){  
  9. thrownewIllegalArgumentException(e.toString());  
  10. }  
  11. //把生成的代理類的類對(duì)象記錄進(jìn)proxyClasses表  
  12. proxyClasses.put(proxyClass,null); 

由此可見,所有的代碼生成的工作都由神秘的ProxyGenerator所完成了,當(dāng)你嘗試去探索這個(gè)類時(shí),你所能獲得的信息僅僅是它位于并未公開的sun.misc包,有若干常量、變量和方法以完成這個(gè)神奇的代碼生成的過程,但是sun并沒有提供源代碼以供研讀。至于動(dòng)態(tài)類的定義,則由Proxy的native靜態(tài)方法defineClass0執(zhí)行。

代碼生成過程進(jìn)入結(jié)尾部分,根據(jù)結(jié)果更新緩存表,如果成功則將代理類的類對(duì)象引用更新進(jìn)緩存表,否則清楚緩存表中對(duì)應(yīng)關(guān)鍵值,最后喚醒所有可能的正在等待的線程。

走完了以上四個(gè)步驟后,至此,所有的代理類生成細(xì)節(jié)都已介紹完畢,剩下的靜態(tài)方法如getInvocationHandler和isProxyClass就顯得如此的直觀,只需通過查詢相關(guān)變量就可以完成,所以對(duì)其的代碼分析就省略了。

代理類實(shí)現(xiàn)推演

分析了Proxy類的源代碼,相信在讀者的腦海中會(huì)對(duì)Java動(dòng)態(tài)代理機(jī)制形成一個(gè)更加清晰的理解,但是,當(dāng)探索之旅在sun.misc.ProxyGenerator類處嘎然而止,所有的神秘都匯聚于此時(shí),相信不少讀者也會(huì)對(duì)這個(gè)ProxyGenerator類產(chǎn)生有類似的疑惑:它到底做了什么呢?它是如何生成動(dòng)態(tài)代理類的代碼的呢?誠(chéng)然,這里也無法給出確切的答案。還是讓我們帶著這些疑惑,一起開始探索之旅吧。

事物往往不像其看起來的復(fù)雜,需要的是我們能夠化繁為簡(jiǎn),這樣也許就能有更多撥云見日的機(jī)會(huì)。拋開所有想象中的未知而復(fù)雜的神秘因素,如果讓我們用最簡(jiǎn)單的方法去實(shí)現(xiàn)一個(gè)代理類,唯一的要求是同樣結(jié)合調(diào)用處理器實(shí)施方法的分派轉(zhuǎn)發(fā),您的第一反應(yīng)將是什么呢?“聽起來似乎并不是很復(fù)雜”。的確,掐指算算所涉及的工作無非包括幾個(gè)反射調(diào)用,以及對(duì)原始類型數(shù)據(jù)的裝箱或拆箱過程,其他的似乎都已經(jīng)水到渠成。非常地好,讓我們整理一下思緒,一起來完成一次完整的推演過程吧。

  1. 清單11.代理類中方法調(diào)用的分派轉(zhuǎn)發(fā)推演實(shí)現(xiàn)  
  2. //假設(shè)需代理接口Simulator  
  3. publicinterfaceSimulator{  
  4. shortsimulate(intarg1,longarg2,Stringarg3)throwsExceptionA,ExceptionB;  
  5. }  
  6.  
  7. //假設(shè)代理類為SimulatorProxy,其類聲明將如下  
  8. finalpublicclassSimulatorProxyimplementsSimulator{  
  9.  
  10. //調(diào)用處理器對(duì)象的引用  
  11. protectedInvocationHandlerhandler;  
  12.  
  13. //以調(diào)用處理器為參數(shù)的構(gòu)造函數(shù)  
  14. publicSimulatorProxy(InvocationHandlerhandler){  
  15. this.handler=handler;  
  16. }  
  17.  
  18. //實(shí)現(xiàn)接口方法simulate  
  19. publicshortsimulate(intarg1,longarg2,Stringarg3)  
  20. throwsExceptionA,ExceptionB{  
  21.  
  22. //第一步是獲取simulate方法的Method對(duì)象  
  23. java.lang.reflect.Methodmethod=null;  
  24. try{  
  25. method=Simulator.class.getMethod(  
  26. "simulate",  
  27. newClass[]{int.class,long.class,String.class});  
  28. }catch(Exceptione){  
  29. //異常處理1(略)  
  30. }  
  31.  
  32. //第二步是調(diào)用handler的invoke方法分派轉(zhuǎn)發(fā)方法調(diào)用  
  33. Objectr=null;  
  34. try{  
  35. r=handler.invoke(this,  
  36. method,  
  37. //對(duì)于原始類型參數(shù)需要進(jìn)行裝箱操作  
  38. newObject[]{newInteger(arg1),newLong(arg2),arg3});  
  39. }catch(Throwablee){  
  40. //異常處理2(略)  
  41. }  
  42. //第三步是返回結(jié)果(返回類型是原始類型則需要進(jìn)行拆箱操作)  
  43. return((Short)r).shortValue();  
  44. }  

模擬推演為了突出通用邏輯所以更多地關(guān)注正常流程,而淡化了錯(cuò)誤處理,但在實(shí)際中錯(cuò)誤處理同樣非常重要。從以上的推演中我們可以得出一個(gè)非常通用的結(jié)構(gòu)化流程:第一步從代理接口獲取被調(diào)用的方法對(duì)象,第二步分派方法到調(diào)用處理器執(zhí)行,第三步返回結(jié)果。

在這之中,所有的信息都是可以已知的,比如接口名、方法名、參數(shù)類型、返回類型以及所需的裝箱和拆箱操作,那么既然我們手工編寫是如此,那又有什么理由不相信ProxyGenerator不會(huì)做類似的實(shí)現(xiàn)呢?至少這是一種比較可能的實(shí)現(xiàn)。

#p#
接下來讓我們把注意力重新回到先前被淡化的錯(cuò)誤處理上來。在異常處理1處,由于我們有理由確保所有的信息如接口名、方法名和參數(shù)類型都準(zhǔn)確無誤,所以這部分異常發(fā)生的概率基本為零,所以基本可以忽略。而異常處理2處,我們需要思考得更多一些。

回想一下,接口方法可能聲明支持一個(gè)異常列表,而調(diào)用處理器invoke方法又可能拋出與接口方法不支持的異常,再回想一下先前提及的Java動(dòng)態(tài)代理的關(guān)于異常處理的特點(diǎn),對(duì)于不支持的異常,必須拋UndeclaredThrowableException運(yùn)行時(shí)異常。所以通過再次推演,我們可以得出一個(gè)更加清晰的異常處理2的情況:

  1. 清單12.細(xì)化的異常處理2  
  2. Objectr=null;  
  3.  
  4. try{  
  5. r=handler.invoke(this,  
  6. method,  
  7. newObject[]{newInteger(arg1),newLong(arg2),arg3});  
  8.  
  9. }catch(ExceptionAe){  
  10.  
  11. //接口方法支持ExceptionA,可以拋出  
  12. throwe;  
  13.  
  14. }catch(ExceptionBe){  
  15. //接口方法支持ExceptionB,可以拋出  
  16. throwe;  
  17.  
  18. }catch(Throwablee){  
  19. //其他不支持的異常,一律拋UndeclaredThrowableException  
  20. thrownewUndeclaredThrowableException(e);  

這樣我們就完成了對(duì)動(dòng)態(tài)代理類的推演實(shí)現(xiàn)。推演實(shí)現(xiàn)遵循了一個(gè)相對(duì)固定的模式,可以適用于任意定義的任何接口,而且代碼生成所需的信息都是可知的,那么有理由相信即使是機(jī)器自動(dòng)編寫的代碼也有可能延續(xù)這樣的風(fēng)格,至少可以保證這是可行的。

美中不足

誠(chéng)然,Proxy已經(jīng)設(shè)計(jì)得非常優(yōu)美,但是還是有一點(diǎn)點(diǎn)小小的遺憾之處,那就是它始終無法擺脫僅支持interface代理的桎梏,因?yàn)樗脑O(shè)計(jì)注定了這個(gè)遺憾。回想一下那些動(dòng)態(tài)生成的代理類的繼承關(guān)系圖,它們已經(jīng)注定有一個(gè)共同的父類叫Proxy。Java的繼承機(jī)制注定了這些動(dòng)態(tài)代理類們無法實(shí)現(xiàn)對(duì)class的動(dòng)態(tài)代理,原因是多繼承在Java中本質(zhì)上就行不通。

有很多條理由,人們可以否定對(duì)class代理的必要性,但是同樣有一些理由,相信支持class動(dòng)態(tài)代理會(huì)更美好。接口和類的劃分,本就不是很明顯,只是到了Java中才變得如此的細(xì)化。如果只從方法的聲明及是否被定義來考量,有一種兩者的混合體,它的名字叫抽象類。實(shí)現(xiàn)對(duì)抽象類的動(dòng)態(tài)代理,相信也有其內(nèi)在的價(jià)值。此外,還有一些歷史遺留的類,它們將因?yàn)闆]有實(shí)現(xiàn)任何接口而從此與動(dòng)態(tài)代理永世無緣。如此種種,不得不說是一個(gè)小小的遺憾。但是,不完美并不等于不偉大,偉大是一種本質(zhì),Java動(dòng)態(tài)代理就是佐例。

【編輯推薦】

  1. Java程序開發(fā)中代理技術(shù)的使用方法
  2. 淺談Java中用動(dòng)態(tài)代理類實(shí)現(xiàn)記憶功能
  3. 探討Java代理模式與反射機(jī)制的實(shí)際應(yīng)用 
責(zé)任編輯:王曉東 來源: IBM
相關(guān)推薦

2015-09-24 08:55:14

Java動(dòng)態(tài)代理擴(kuò)展

2015-09-24 08:54:36

java動(dòng)態(tài)代理

2015-09-28 15:59:00

Java動(dòng)態(tài)代理機(jī)制

2012-02-08 10:37:42

Java反射

2011-03-23 10:40:51

java代理模式

2012-02-08 10:12:19

Java反射

2009-05-19 15:01:12

WLANWEPWAPI

2011-04-06 11:41:25

Java動(dòng)態(tài)代理

2012-08-28 10:59:26

JavaJava動(dòng)態(tài)代理Proxy

2017-10-12 14:56:11

2023-12-06 08:23:44

代理模式設(shè)計(jì)模式

2015-09-22 11:09:47

Java 8動(dòng)態(tài)代理

2021-07-06 06:39:22

Java靜態(tài)代理動(dòng)態(tài)代理

2012-01-09 11:26:15

Java

2017-05-11 21:30:01

Android動(dòng)態(tài)代理ServiceHook

2023-02-24 07:42:30

Java動(dòng)態(tài)代理

2011-11-17 14:32:45

Java靜態(tài)代理動(dòng)態(tài)代理

2016-12-14 14:29:30

Java動(dòng)態(tài)綁定機(jī)制

2010-01-06 10:04:54

軟交換技術(shù)

2014-12-04 10:30:04

Java
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

av影院午夜一区| 午夜激情福利在线| 都市激情亚洲欧美| 日本高清无吗v一区| 亚洲 中文字幕 日韩 无码| 午夜av一区| www.午夜精品| 成年网站在线视频网站| 一区二区三区波多野结衣在线观看| 一区二区三区在线视频111| 91麻豆国产自产在线观看亚洲| 美女av一区二区三区 | 三级在线视频| 波多野洁衣一区| 日韩在线国产| 在线亚洲免费| 国产日本欧美一区二区三区在线| 懂色av色香蕉一区二区蜜桃| 日韩av在线免费观看| 免费观看成人高潮| 色婷婷av一区二区三区gif| 女人体1963| 欧美激情中文字幕| 亚洲国产精品久久久久婷蜜芽| 久久精品72免费观看| 久久久99爱| 91精品动漫在线观看| 日韩69视频在线观看| 在线一区二区三区视频| 深夜福利亚洲导航| 99re久久| 亚洲小视频在线观看| 三级成人黄色影院| 日韩欧美高清在线| av在线理伦电影| 欧美精品一区二区在线观看| 毛片在线不卡| 欧美精品一区二区三| 在线欧美三级| 亚洲成色777777女色窝| 影音先锋在线视频| 精品久久久久久亚洲综合网| 欧美激情成人动漫| 亚洲国产精品999| free性m.freesex欧美| 精品乱码亚洲一区二区不卡| www.久久热.com| 欧美精品粉嫩高潮一区二区| 美女国产在线| 日韩精品资源二区在线| 视频在线这里都是精品| 日韩精品欧美激情| 国产精品久久久久久妇女| 久久精品福利视频| 精品资源在线| 亚洲va电影大全| 女人色偷偷aa久久天堂| 欧洲精品国产| 日本午夜一区二区| 一区二区在线观| av在线一区二区| www.色就是色| 图片区小说区国产精品视频| 黄色国产网站在线播放| 日韩av在线免费看| 88久久精品| 亚洲a中文字幕| 日韩av中文在线观看| 欧美日韩二三区| 亚洲综合色在线| 新版中文在线官网| 久久久国产一区| 91精品国产乱码久久久久久| 日韩在线电影一区| 国产三级欧美三级| 国际av在线| 亚洲午夜小视频| 欧美精品尤物在线观看| 视频在线99| 中文一区二区完整视频在线观看 | 国产欧美精品日韩精品| 日本成人在线电影网| 欧美成人福利在线观看| 欧美日韩国产影片| 欧美天堂一区二区| 国产99午夜精品一区二区三区| 国产乱码精品1区2区3区| 日本高清好狼色视频| 日韩一级黄色大片| 国产精品对白久久久久粗| 欧美激情国产日韩| 国产精品久久久久久一区二区三区 | 福利视频一二区| 欧美日韩性视频| 欧美日韩免费看片| 国产一区二区视频在线观看| 国产精品综合在线视频| 婷婷六月激情| 日日摸夜夜添一区| 亚洲国产高清一区| 五月婷婷六月合| 亚洲精品大尺度| 伊人久久大香线蕉综合四虎小说| 九色在线视频观看| 日韩精品一区二区三区视频播放| 国产剧情一区| 欧美成人高潮一二区在线看| 日韩午夜av一区| 午夜电影亚洲| 成人影片在线播放| 亚洲欧洲国产日本综合| 欧美成人黑人| 久久国产精品99久久久久久丝袜| 一区二区三区四区蜜桃| 亚洲91网站| 人人干视频在线| 亚洲福利视频在线| 国产亚洲毛片| 黄色大片在线看| 国产精品国产三级国产专播精品人 | 日本私人影院在线观看| 欧美激情一二区| 国产精品一区不卡| 欧美巨大xxxx做受沙滩| 久久99影院| 欧美日韩高清一区二区不卡| 久久视频在线| 91美女在线免费观看| 欧美日韩国产123| 成人黄色国产精品网站大全在线免费观看 | 91精彩视频在线观看| 成人精品久久久| 香蕉成人伊视频在线观看| 看亚洲a级一级毛片| 国产精品免费看久久久无码| 日韩精品一区二区三区老鸭窝| 欧美另类专区| 国产精品毛片一区二区三区四区| 91免费欧美精品| 欧美日韩性视频在线| 国内精品久久久久久久影视蜜臀| 青春有你2免费观看完整版在线播放高清| 国产精品男女猛烈高潮激情| 亚洲成人tv网| 午夜激情久久| 午夜在线观看91| 国产一区二区视频在线免费观看 | 亚洲欧洲综合| 欧洲日本在线| 日韩国产在线一区| 亚洲国产欧美一区二区三区同亚洲| 免费成人在线视频观看| 午夜伦理福利在线| 韩日视频在线观看| 欧美成人自拍视频| 国产精品乱人伦| 麻豆av免费在线观看| 日本不卡二区| 最近2019年好看中文字幕视频| 99久久综合色| 欧美电影完整版在线观看| 狠狠操在线视频| 久久66热这里只有精品| 日韩精品中文字| 久久夜色精品国产噜噜av| 亚洲最大在线| 国产一级二级三级在线观看| 久久99精品久久久久久久久久 | 日韩免费网站| 福利在线一区二区| 久久久久久久一区二区| 欧美日韩美女在线观看| 日韩一级免费| 性欧美freehd18| 国产igao激情在线入口| 国产一区二区三区高清| 亚洲国产高潮在线观看| 91麻豆6部合集magnet| 欧美日韩中字| 91视频欧美| 好男人看片在线观看免费观看国语| 国产精品露出视频| 亚洲视频网站在线观看| 国产精品久久久久aaaa樱花 | 国产高潮av| 亚洲美女搞黄| 国产999精品| 亚洲成av人影院在线观看| 中文字幕亚洲一区二区av在线 | 欧美视频网站| 狠狠久久综合| 日韩福利一区二区| 欧美午夜性视频| 91免费在线视频| 在线成人激情视频| 一本大道综合伊人精品热热| 99久久综合精品| 亚洲国产一区二区精品专区| 亚洲一区二区三区中文字幕在线观看| 国产一级免费在线观看|