国产成人精品18p,天天干成人网,无码专区狠狠躁天天躁,美女脱精光隐私扒开免费观看

Java中動(dòng)態(tài)代理機制的原理是什么

發(fā)布時(shí)間:2021-07-04 20:13 來(lái)源:億速云 閱讀:0 作者:Leah 欄目: 開(kāi)發(fā)技術(shù)

今天就跟大家聊聊有關(guān)Java中動(dòng)態(tài)代理機制的原理是什么,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結了以下內容,希望大家根據這篇文章可以有所收獲。

Java 動(dòng)態(tài)代理機制的出現,使得 Java  開(kāi)發(fā)人員不用手工編寫(xiě)代理類(lèi),只要簡(jiǎn)單地指定一組接口及委托類(lèi)對象,便能動(dòng)態(tài)地獲得代理類(lèi)。代理類(lèi)會(huì )負責將所有的方法調用分派到委托對象上反射執行,在分 派執行的過(guò)程中,開(kāi)發(fā)人員還可以按需調整委托類(lèi)對象及其功能,這是一套非常靈活有彈性的代理框架。通過(guò)閱讀本文,讀者將會(huì )對 Java  動(dòng)態(tài)代理機制有更加深入的理解。本文首先從 Java 動(dòng)態(tài)代理的運行機制和特點(diǎn)出發(fā),對其代碼進(jìn)行了分析,推演了動(dòng)態(tài)生成類(lèi)的內部實(shí)現。

代理:設計模式

代理是一種常用的設計模式,其目的就是為其他對象提供一個(gè)代理以控制對某個(gè)對象的訪(fǎng)問(wèn)。代理類(lèi)負責為委托類(lèi)預處理消息,過(guò)濾消息并轉發(fā)消息,以及進(jìn)行消息被委托類(lèi)執行后的后續處理。

圖 1. 代理模式

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

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

要了解 Java 動(dòng)態(tài)代理的機制,首先需要了解以下相關(guān)的類(lèi)或接口:

  • java.lang.reflect.Proxy:這是 Java 動(dòng)態(tài)代理機制的主類(lèi),它提供了一組靜態(tài)方法來(lái)為一組接口動(dòng)態(tài)地生成代理類(lèi)及其對象。

清單 1. Proxy 的靜態(tài)方法

// 方法 1: 該方法用于獲取指定代理對象所關(guān)聯(lián)的調用處理器
static InvocationHandler getInvocationHandler(Object proxy)

// 方法 2:該方法用于獲取關(guān)聯(lián)于指定類(lèi)裝載器和一組接口的動(dòng)態(tài)代理類(lèi)的類(lèi)對象
static Class getProxyClass(ClassLoader loader, Class[] interfaces)

// 方法 3:該方法用于判斷指定類(lèi)對象是否是一個(gè)動(dòng)態(tài)代理類(lèi)
static boolean isProxyClass(Class cl)

// 方法 4:該方法用于為指定類(lèi)裝載器、一組接口及調用處理器生成動(dòng)態(tài)代理類(lèi)實(shí)例
static Object newProxyInstance(ClassLoader loader, Class[] interfaces,
    InvocationHandler h)

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

清單 2. InvocationHandler 的核心方法

// 該方法負責集中處理動(dòng)態(tài)代理類(lèi)上的所有方法調用。***個(gè)參數既是代理類(lèi)實(shí)例,第二個(gè)參數是被調用的方法對象
// 第三個(gè)方法是調用參數。調用處理器根據這三個(gè)參數進(jìn)行預處理或分派到委托類(lèi)實(shí)例上發(fā)射執行
Object invoke(Object proxy, Method method, Object[] args)

每次生成動(dòng)態(tài)代理類(lèi)對象時(shí)都需要指定一個(gè)實(shí)現了該接口的調用處理器對象(參見(jiàn) Proxy 靜態(tài)方法 4 的第三個(gè)參數)。

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

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

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

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

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

  3. 通過(guò)反射機制獲得動(dòng)態(tài)代理類(lèi)的構造函數,其唯一參數類(lèi)型是調用處理器接口類(lèi)型;

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

清單 3. 動(dòng)態(tài)代理對象創(chuàng )建過(guò)程

// InvocationHandlerImpl 實(shí)現了 InvocationHandler 接口,并能實(shí)現方法調用從代理類(lèi)到委托類(lèi)的分派轉發(fā)
// 其內部通常包含指向委托類(lèi)實(shí)例的引用,用于真正執行分派轉發(fā)過(guò)來(lái)的方法調用
InvocationHandler handler = new InvocationHandlerImpl(..);

// 通過(guò) Proxy 為包括 Interface 接口在內的一組接口動(dòng)態(tài)創(chuàng )建代理類(lèi)的類(lèi)對象
Class clazz = Proxy.getProxyClass(classLoader, new Class[] { Interface.class, ... });

// 通過(guò)反射從生成的類(lèi)對象獲得構造函數對象
Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class });

// 通過(guò)構造函數對象創(chuàng )建動(dòng)態(tài)代理類(lèi)實(shí)例
Interface Proxy = (Interface)constructor.newInstance(new Object[] { handler });

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

清單 4. 簡(jiǎn)化的動(dòng)態(tài)代理對象創(chuàng )建過(guò)程

// InvocationHandlerImpl 實(shí)現了 InvocationHandler 接口,并能實(shí)現方法調用從代理類(lèi)到委托類(lèi)的分派轉發(fā)
InvocationHandler handler = new InvocationHandlerImpl(..);

// 通過(guò) Proxy 直接創(chuàng )建動(dòng)態(tài)代理類(lèi)實(shí)例
Interface proxy = (Interface)Proxy.newProxyInstance( classLoader,
  new Class[] { Interface.class },
  handler );

接下來(lái)讓我們來(lái)了解一下 Java 動(dòng)態(tài)代理機制的一些特點(diǎn)。

首先是動(dòng)態(tài)生成的代理類(lèi)本身的一些特點(diǎn)。1)包:如果所代理的接口都是 public  的,那么它將被定義在頂層包(即包路徑為空),如果所代理的接口中有非 public 的接口(因為接口不能被定義為 protect 或  private,所以除 public 之外就是默認的 package 訪(fǎng)問(wèn)級別),那么它將被定義在該接口所在包(假設代理了  com.ibm.developerworks 包中的某非 public 接口 A,那么新生成的代理類(lèi)所在的包就是  com.ibm.developerworks),這樣設計的目的是為了***程度的保證動(dòng)態(tài)代理類(lèi)不會(huì )因為包管理的問(wèn)題而無(wú)法被成功定義并訪(fǎng)問(wèn);2)類(lèi)修 飾符:該代理類(lèi)具有 final 和 public  修飾符,意味著(zhù)它可以被所有的類(lèi)訪(fǎng)問(wèn),但是不能被再度繼承;3)類(lèi)名:格式是“$ProxyN”,其中 N 是一個(gè)逐一遞增的阿拉伯數字,代表  Proxy 類(lèi)第 N 次生成的動(dòng)態(tài)代理類(lèi),值得注意的一點(diǎn)是,并不是每次調用 Proxy 的靜態(tài)方法創(chuàng )建動(dòng)態(tài)代理類(lèi)都會(huì )使得 N  值增加,原因是如果對同一組接口(包括接口排列的順序相同)試圖重復創(chuàng )建動(dòng)態(tài)代理類(lèi),它會(huì )很聰明地返回先前已經(jīng)創(chuàng )建好的代理類(lèi)的類(lèi)對象,而不會(huì )再?lài)L試去創(chuàng ) 建一個(gè)全新的代理類(lèi),這樣可以節省不必要的代碼重復生成,提高了代理類(lèi)的創(chuàng )建效率。4)類(lèi)繼承關(guān)系:該類(lèi)的繼承關(guān)系如圖:

圖 2. 動(dòng)態(tài)代理類(lèi)的繼承圖

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

接下來(lái)讓我們了解一下代理類(lèi)實(shí)例的一些特點(diǎn)。每個(gè)實(shí)例都會(huì )關(guān)聯(lián)一個(gè)調用處理器對象,可以通過(guò) Proxy 提供的靜態(tài)方法  getInvocationHandler  去獲得代理類(lèi)實(shí)例的調用處理器對象。在代理類(lèi)實(shí)例上調用其代理的接口中所聲明的方法時(shí),這些方法最終都會(huì )由調用處理器的 invoke  方法執行,此外,值得注意的是,代理類(lèi)的根類(lèi) java.lang.Object 中有三個(gè)方法也同樣會(huì )被分派到調用處理器的 invoke  方法執行,它們是 hashCode,equals 和 toString,可能的原因有:一是因為這些方法為 public 且非 final  類(lèi)型,能夠被代理類(lèi)覆蓋;二是因為這些方法往往呈現出一個(gè)類(lèi)的某種特征屬性,具有一定的區分度,所以為了保證代理類(lèi)與委托類(lèi)對外的一致性,這三個(gè)方法也應 該被分派到委托類(lèi)執行。當代理的一組接口有重復聲明的方法且該方法被調用時(shí),代理類(lèi)總是從排在最前面的接口中獲取方法對象并分派給調用處理器,而無(wú)論代理 類(lèi)實(shí)例是否正在以該接口(或繼承于該接口的某子接口)的形式被外部引用,因為在代理類(lèi)內部無(wú)法區分其當前的被引用類(lèi)型。

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

***再來(lái)了解一下異常處理方面的特點(diǎn)。從調用處理器接口聲明的方法中可以看到理論上它能夠拋出任何類(lèi)型的異常,因為所有的異常都繼承于  Throwable  接口,但事實(shí)是否如此呢?答案是否定的,原因是我們必須遵守一個(gè)繼承原則:即子類(lèi)覆蓋父類(lèi)或實(shí)現父接口的方法時(shí),拋出的異常必須在原方法支持的異常列表之 內。所以雖然調用處理器理論上講能夠,但實(shí)際上往往受限制,除非父接口中的方法支持拋 Throwable 異常。那么如果在 invoke  方法中的確產(chǎn)生了接口方法聲明中不支持的異常,那將如何呢?放心,Java 動(dòng)態(tài)代理類(lèi)已經(jīng)為我們設計好了解決方法:它將會(huì )拋出  UndeclaredThrowableException 異常。這個(gè)異常是一個(gè) RuntimeException  類(lèi)型,所以不會(huì )引起編譯錯誤。通過(guò)該異常的 getCause 方法,還可以獲得原來(lái)那個(gè)不受支持的異常對象,以便于錯誤診斷。

代碼是***的老師

機制和特點(diǎn)都介紹過(guò)了,接下來(lái)讓我們通過(guò)源代碼來(lái)了解一下 Proxy 到底是如何實(shí)現的。

首先記住 Proxy 的幾個(gè)重要的靜態(tài)變量:

清單 5. Proxy 的重要靜態(tài)變量

// 映射表:用于維護類(lèi)裝載器對象到其對應的代理類(lèi)緩存
private static Map loaderToCache = new WeakHashMap();

// 標記:用于標記一個(gè)動(dòng)態(tài)代理類(lèi)正在被創(chuàng )建中
private static Object pendingGenerationMarker = new Object();

// 同步表:記錄已經(jīng)被創(chuàng )建的動(dòng)態(tài)代理類(lèi)類(lèi)型,主要被方法 isProxyClass 進(jìn)行相關(guān)的判斷
private static Map proxyClasses = Collections.synchronizedMap(new WeakHashMap());

// 關(guān)聯(lián)的調用處理器引用
protected InvocationHandler h;

然后,來(lái)看一下 Proxy 的構造方法:

清單 6. Proxy 構造方法

// 由于 Proxy 內部從不直接調用構造函數,所以 private 類(lèi)型意味著(zhù)禁止任何調用
private Proxy() {}

// 由于 Proxy 內部從不直接調用構造函數,所以 protected 意味著(zhù)只有子類(lèi)可以調用
protected Proxy(InvocationHandler h) {this.h = h;}

接著(zhù),可以快速瀏覽一下 newProxyInstance 方法,因為其相當簡(jiǎn)單:

清單 7. Proxy 靜態(tài)方法 newProxyInstance

public static Object newProxyInstance(ClassLoader loader,             Class<?>[] interfaces,             InvocationHandler h)             throws IllegalArgumentException {      // 檢查 h 不為空,否則拋異常     if (h == null) {         throw new NullPointerException();     }      // 獲得與制定類(lèi)裝載器和一組接口相關(guān)的代理類(lèi)類(lèi)型對象     Class cl = getProxyClass(loader, interfaces);      // 通過(guò)反射獲取構造函數對象并生成代理類(lèi)實(shí)例     try {         Constructor cons = cl.getConstructor(constructorParams);         return (Object) cons.newInstance(new Object[] { h });     } catch (NoSuchMethodException e) { throw new InternalError(e.toString());     } catch (IllegalAccessException e) { throw new InternalError(e.toString());     } catch (InstantiationException e) { throw new InternalError(e.toString());     } catch (InvocationTargetException e) { throw new InternalError(e.toString());     } }

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

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

    清單 8. 通過(guò) Class.forName 方法判接口的可見(jiàn)性


    1. try { 

    2.     // 指定接口名字、類(lèi)裝載器對象,同時(shí)制定 initializeBoolean 為 false 表示無(wú)須初始化類(lèi) 

    3.     // 如果方法返回正常這表示可見(jiàn),否則會(huì )拋出 ClassNotFoundException 異常表示不可見(jiàn) 

    4.     interfaceClass = Class.forName(interfaceName, false, loader); 

    5. } catch (ClassNotFoundException e) { 

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

    清單 9. 緩存表的使用


    1. do { 

    2.     // 以接口名字列表作為關(guān)鍵字獲得對應 cache 值 

    3.     Object value = cache.get(key); 

    4.     if (value instanceof Reference) { 

    5.         proxyClass = (Class) ((Reference) value).get(); 

    6.     } 

    7.     if (proxyClass != null) { 

    8.         // 如果已經(jīng)創(chuàng )建,直接返回 

    9.         return proxyClass; 

    10.     } else if (value == pendingGenerationMarker) { 

    11.         // 代理類(lèi)正在被創(chuàng )建,保持等待 

    12.         try { 

    13.             cache.wait(); 

    14.         } catch (InterruptedException e) { 

    15.         } 

    16.         // 等待被喚醒,繼續循環(huán)并通過(guò)二次檢查以確保創(chuàng )建完成,否則重新等待 

    17.         continue; 

    18.     } else { 

    19.         // 標記代理類(lèi)正在被創(chuàng )建 

    20.         cache.put(key, pendingGenerationMarker); 

    21.         // break 跳出循環(huán)已進(jìn)入創(chuàng )建過(guò)程 

    22.         break; 

    23. } while (true); 

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

    清單 10. 動(dòng)態(tài)生成代理類(lèi)


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

    1. // 動(dòng)態(tài)地生成代理類(lèi)的字節碼數組 

    2. byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces); 

    3. try { 

    4.     // 動(dòng)態(tài)地定義新生成的代理類(lèi) 

    5.     proxyClass = defineClass0(loader, proxyName, proxyClassFile, 0, 

    6.         proxyClassFile.length); 

    7. } catch (ClassFormatError e) { 

    8.     throw new IllegalArgumentException(e.toString()); 

    9.  

    10. // 把生成的代理類(lèi)的類(lèi)對象記錄進(jìn) proxyClasses 表 

    11. proxyClasses.put(proxyClass, null); 

  4. 代碼生成過(guò)程進(jìn)入結尾部分,根據結果更新緩存表,如果成功則將代理類(lèi)的類(lèi)對象引用更新進(jìn)緩存表,否則清楚緩存表中對應關(guān)鍵值,***喚醒所有可能的正在等待的線(xiàn)程。

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

代理類(lèi)實(shí)現推演

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

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

清單 11. 代理類(lèi)中方法調用的分派轉發(fā)推演實(shí)現

// 假設需代理接口 Simulator public interface Simulator {     short simulate(int arg1, long arg2, String arg3) throws ExceptionA, ExceptionB; }  // 假設代理類(lèi)為 SimulatorProxy, 其類(lèi)聲明將如下 final public class SimulatorProxy implements Simulator {      // 調用處理器對象的引用     protected InvocationHandler handler;      // 以調用處理器為參數的構造函數     public SimulatorProxy(InvocationHandler handler){         this.handler = handler;     }      // 實(shí)現接口方法 simulate     public short simulate(int arg1, long arg2, String arg3)         throws ExceptionA, ExceptionB {          // ***步是獲取 simulate 方法的 Method 對象         java.lang.reflect.Method method = null;         try{             method = Simulator.class.getMethod(                 "simulate",                 new Class[] {int.class, long.class, String.class} );         } catch(Exception e) {             // 異常處理 1(略)         }          // 第二步是調用 handler 的 invoke 方法分派轉發(fā)方法調用         Object r = null;         try {             r = handler.invoke(this,                 method,                 // 對于原始類(lèi)型參數需要進(jìn)行裝箱操作                 new Object[] {new Integer(arg1), new Long(arg2), arg3});         }catch(Throwable e) {             // 異常處理 2(略)         }         // 第三步是返回結果(返回類(lèi)型是原始類(lèi)型則需要進(jìn)行拆箱操作)         return ((Short)r).shortValue();     } }

模擬推演為了突出通用邏輯所以更多地關(guān)注正常流程,而淡化了錯誤處理,但在實(shí)際中錯誤處理同樣非常重要。從以上的推演中我們可以得出一個(gè)非常通用的 結構化流程:***步從代理接口獲取被調用的方法對象,第二步分派方法到調用處理器執行,第三步返回結果。在這之中,所有的信息都是可以已知的,比如接口 名、方法名、參數類(lèi)型、返回類(lèi)型以及所需的裝箱和拆箱操作,那么既然我們手工編寫(xiě)是如此,那又有什么理由不相信 ProxyGenerator  不會(huì )做類(lèi)似的實(shí)現呢?至少這是一種比較可能的實(shí)現。

接下來(lái)讓我們把注意力重新回到先前被淡化的錯誤處理上來(lái)。在異常處理 1  處,由于我們有理由確保所有的信息如接口名、方法名和參數類(lèi)型都準確無(wú)誤,所以這部分異常發(fā)生的概率基本為零,所以基本可以忽略。而異常處理 2  處,我們需要思考得更多一些?;叵胍幌?,接口方法可能聲明支持一個(gè)異常列表,而調用處理器 invoke  方法又可能拋出與接口方法不支持的異常,再回想一下先前提及的 Java 動(dòng)態(tài)代理的關(guān)于異常處理的特點(diǎn),對于不支持的異常,必須拋  UndeclaredThrowableException 運行時(shí)異常。所以通過(guò)再次推演,我們可以得出一個(gè)更加清晰的異常處理 2 的情況:

清單 12. 細化的異常處理 2

Object r = null;  try {     r = handler.invoke(this,         method,         new Object[] {new Integer(arg1), new Long(arg2), arg3});  } catch( ExceptionA e) {      // 接口方法支持 ExceptionA,可以?huà)伋?nbsp;    throw e;  } catch( ExceptionB e ) {     // 接口方法支持 ExceptionB,可以?huà)伋?nbsp;    throw e;  } catch(Throwable e) {     // 其他不支持的異常,一律拋 UndeclaredThrowableException     throw new UndeclaredThrowableException(e); }

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

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng )、來(lái)自互聯(lián)網(wǎng)轉載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權請聯(lián)系QQ:712375056 進(jìn)行舉報,并提供相關(guān)證據,一經(jīng)查實(shí),將立刻刪除涉嫌侵權內容。

野外性XXXXFREEXXXXX户外| 日韩精品无码综合福利网| 中国精学生妹品射精久久| 亚洲中文字幕乱码一区| 岳肥肉紧嫩嫩伦69| 2014av天堂影音先锋|