Proxy類的API描述(JDK API 1.6裡扒下來的)
java.lang.reflect
類 Proxy
java.lang.Object
-java.lang.reflect.Proxy
所有已實現的介面:
public class Proxy
Proxy
提供用於建立動態代理類和例項的靜態方法,它還是由這些方法建立的所有動態代理類的超類。
建立某一介面 Foo
的代理:
InvocationHandler handler = new MyInvocationHandler(...); Class proxyClass = Proxy.getProxyClass( Foo.class.getClassLoader(), new Class[] { Foo.class }); Foo f = (Foo) proxyClass. getConstructor(new Class[] { InvocationHandler.class }). newInstance(new Object[] { handler });
或使用以下更簡單的方法:
Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
new Class[] { Foo.class },
handler);
動態代理類(以下簡稱為代理類)是一個實現在建立類時在執行時指定的介面列表的類,該類具有下面描述的行為。 代理介面 是代理類實現的一個介面。 代理例項 是代理類的一個例項。 每個代理例項都有一個關聯的呼叫處理程式
InvocationHandler
。通過其中一個代理介面的代理例項上的方法呼叫將被指派到例項的呼叫處理程式的 Invoke
方法,並傳遞代理例項、識別呼叫方法的 java.lang.reflect.Method
物件以及包含引數的 Object
型別的陣列。呼叫處理程式以適當的方式處理編碼的方法呼叫,並且它返回的結果將作為代理例項上方法呼叫的結果返回。
代理類具用以下屬性:
- 代理類是公共的、最終的,而不是抽象的。
- 未指定代理類的非限定名稱。但是,以字串
"$Proxy"
開頭的類名空間應該為代理類保留。 - 代理類擴充套件
java.lang.reflect.Proxy
- 代理類會按同一順序準確地實現其建立時指定的介面。
- 如果代理類實現了非公共介面,那麼它將在與該介面相同的包中定義。否則,代理類的包也是未指定的。注意,包密封將不阻止代理類在執行時在特定包中的成功定義,也不會阻止相同類載入器和帶有特定簽名的包所定義的類。
- 由於代理類將實現所有在其建立時指定的介面,所以對其
Class
物件呼叫getInterfaces
將返回一個包含相同介面列表的陣列(按其建立時指定的順序),對其Class
物件呼叫getMethods
將返回一個包括這些介面中所有方法的Method
物件的陣列,並且呼叫getMethod
將會在代理介面中找到期望的一些方法。 - 如果
Proxy.isProxyClass
方法傳遞代理類(由Proxy.getProxyClass
返回的類,或由Proxy.newProxyInstance
返回的物件的類),則該方法返回 true,否則返回 false。 - 代理類的
java.security.ProtectionDomain
與由引導類載入器(如java.lang.Object
)載入的系統類相同,原因是代理類的程式碼由受信任的系統程式碼生成。此保護域通常被授予java.security.AllPermission
。 - 每個代理類都有一個可以帶一個引數(介面
InvocationHandler
的實現)的公共構造方法,用於設定代理例項的呼叫處理程式。並非必須使用反射 API 才能訪問公共構造方法,通過呼叫Proxy.newInstance
方法(將呼叫Proxy.getProxyClass
的操作和呼叫帶有呼叫處理程式的構造方法結合在一起)也可以建立代理例項。
代理例項具有以下屬性:
- 提供代理例項
proxy
和一個由其代理類Foo
實現的介面,以下表達式將返回 true:proxy instanceof Foo
ClassCastException
):(Foo) proxy
- 每個代理例項都有一個關聯的呼叫處理程式,它會被傳遞到其構造方法中。靜態
Proxy.getInvocationHandler
方法將返回與作為其引數傳遞的代理例項相關的呼叫處理程式。 - 代理例項上的介面方法呼叫將按照該方法的文件描述進行編碼,並被指派到呼叫處理程式的
Invoke
方法。 - 在代理例項上的
java.lang.Object
中宣告的hashCode
、equals
或toString
方法的呼叫將按照與編碼和指派介面方法呼叫相同的方式進行編碼,並被指派到呼叫處理程式的invoke
方法,如上所述。傳遞到invoke
的Method
物件的宣告類是java.lang.Object
。代理類不重寫從java.lang.Object
繼承的代理例項的其他公共方法,所以這些方法的呼叫行為與其對java.lang.Object
例項的操作一樣。
在多代理介面中重複的方法
當代理類的兩個或多個介面包含一個具有相同名稱和引數簽名的方法時,代理類的介面順序變得非常重要。在代理例項上呼叫重複方法 時,傳遞到呼叫處理程式的 Method
物件沒有必要成為其宣告類可以從介面(通過該介面呼叫代理方法)的引用型別指派的物件。此限制存在的原因是,生成的代理類中的相應方法實現無法確定它通過哪一個介面呼叫。因此,在代理例項上呼叫重複方法時,第一個介面中的方法的 Method
物件包含介面的代理類列表中的方法(直接或通過超級介面繼承),該物件會傳遞到呼叫處理程式的 invoke
方法,無論該方法呼叫通過哪一種引用型別發生。
如果代理介面包含某一方法,它的名稱和引數簽名與 java.lang.Object
的 hashCode
、equals
或 toString
方法相同,那麼在代理例項上呼叫這樣的方法時,傳遞到呼叫處理程式的 Method
物件將使 java.lang.Object
成為其宣告類。換句話說,java.lang.Object
公共的非最終方法理論上在所有代理介面之前,以便確定哪一個 Method
物件傳遞到呼叫處理程式。
還要注意,當重複方法被指派到呼叫處理程式時,invoke
方法只可以丟擲經過檢查的異常型別,該異常型別可以使用所有 代理介面(可以通過它呼叫)中方法的 throws
子句指派一種異常型別。如果 invoke
方法丟擲一個經過檢查的異常,該異常沒有指派給任何由一個代理介面(可以通過它呼叫)中的方法宣告的異常型別,那麼該代理例項上的呼叫將丟擲一個未經檢查的 UndeclaredThrowableException
。此限制表示並非所有的由傳遞到 invoke
方法的 Method
物件上呼叫 getExceptionTypes
返回的異常型別都可以由 invoke
方法成功丟擲。
從以下版本開始:
1.3
另請參見:
欄位摘要 |
---|
構造方法摘要 | |
---|---|
protected |
使用其呼叫處理程式的指定值從子類(通常為動態代理類)構建新的 Proxy 例項。 |
方法摘要 | |
---|---|
static Class<?> |
返回代理類的 java.lang.Class 物件,並向其提供類載入器和介面陣列。 |
static boolean |
當且僅當指定的類通過 getProxyClass 方法或 newProxyInstance 方法動態生成為代理類時,返回 true。 |
返回一個指定介面的代理類例項,該介面可以將方法呼叫指派到指定的呼叫處理程式。 |
欄位詳細資訊 |
---|
h
protected InvocationHandler h
此代理例項的呼叫處理程式。
構造方法詳細資訊 |
---|
Proxy
protected Proxy(InvocationHandler h)
使用其呼叫處理程式的指定值從子類(通常為動態代理類)構建新的 Proxy
例項。
引數:
h
- 此代理例項的呼叫處理程式
方法詳細資訊 |
---|
getProxyClass
public static Class<?> getProxyClass(ClassLoader loader,
Class<?>... interfaces)
throws IllegalArgumentException
返回代理類的 java.lang.Class
物件,並向其提供類載入器和介面陣列。該代理類將由指定的類載入器定義,並將實現提供的所有介面。如果類載入器已經定義了具有相同排列介面的代理類,那麼現有的代理類將被返回;否則,類載入器將動態生成並定義這些介面的代理類。
對可以傳遞給 Proxy.getProxyClass
的引數有以下幾個限制:
interfaces
陣列中的所有Class
物件必須表示介面,而不能表示類或基本型別。interfaces
陣列中的兩個元素不能引用同一Class
物件。- 所有介面型別的名稱通過特定的類載入器必須可見。換句話說,對於類載入器
cl
和所有介面i
,以下表達式必須為 true:Class.forName(i.getName(), false, cl) == i
- 所有非公共介面必須位於同一包中;否則,該代理類將不可能實現所有的介面,無論它在哪一個包中定義。
- 對於有相同簽名的指定介面中任何成員方法集:
- 如果任何方法的返回型別是基本型別或 void,那麼所有的方法必須具有與此相同的返回型別。
- 否則,該方法之一必須是返回型別,它可以指派給該方法其餘的所有返回型別。
- 得到的代理類必須不超過虛擬機器在類上施加的任何限制。例如,虛擬機器可以限制某一類實現至多 65535 的介面數;在這種情況下,
interfaces
陣列的大小必須不超過 65535。
如果違反了這些限制,Proxy.getProxyClass
將丟擲 IllegalArgumentException
。如果 interfaces
陣列引數或其任何元素為 null
,則將丟擲 NullPointerException
。
注意,指定的代理介面的順序非常重要:對介面組合相同但順序不同的代理類的兩個請求會導致兩個不同的代理類。
引數:
loader
- 定義代理類的類載入器
interfaces
- 代理類要實現的介面列表
返回:
用指定的類載入器定義的代理類,它可以實現指定的介面
丟擲:
newProxyInstance
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
返回一個指定介面的代理類例項,該介面可以將方法呼叫指派到指定的呼叫處理程式。此方法相當於:
Proxy.getProxyClass(loader, interfaces).
getConstructor(new Class[] { InvocationHandler.class }).
newInstance(new Object[] { handler });
Proxy.newProxyInstance
丟擲 IllegalArgumentException
,原因與 Proxy.getProxyClass
相同。
引數:
loader
- 定義代理類的類載入器
interfaces
- 代理類要實現的介面列表
h
- 指派方法呼叫的呼叫處理程式
返回:
一個帶有代理類的指定呼叫處理程式的代理例項,它由指定的類載入器定義,並實現指定的介面
丟擲:
- 如果 interfaces
陣列引數或其任何元素為 null
,或如果呼叫處理程式 h
為 null
isProxyClass
public static boolean isProxyClass(Class<?> cl)
當且僅當指定的類通過 getProxyClass
方法或 newProxyInstance
方法動態生成為代理類時,返回 true。
此方法的可靠性對於使用它做出安全決策而言非常重要,所以此方法的實現不應僅測試相關的類是否可以擴充套件 Proxy
。
引數:
cl
- 要測試的類
返回:
如該類為代理類,則為 true
,否則為 false
丟擲:
getInvocationHandler
public static InvocationHandler getInvocationHandler(Object proxy)
throws IllegalArgumentException
返回指定代理例項的呼叫處理程式。
引數:
proxy
- 返回呼叫處理程式的代理例項
返回:
代理例項的呼叫處理程式
丟擲: