天天看點

黑馬程式員 動态代理

java教育訓練、android教育訓練期待與您交流!

一,什麼是動态代理?

    動态代理就是使用反射動态的實作一個類的加載進而實作動态執行個體化一個類的對象的效果。

二,怎樣實作動态代理?

    1,首先讓代理類實作InvocationHandler接口,此接口中的invoke方法如下:

Object invoke(Object proxy,

              Method method,

              Object[] args)

              throws Throwable在代理執行個體上處理方法調用并傳回結果。在與方法關聯的代理執行個體上調用方法時,将在調用處理程式上調用此方法。

參數:

proxy - 在其上調用方法的代理執行個體

method - 對應于在代理執行個體上調用的接口方法的 Method 執行個體。Method 對象的聲明類将是在其中聲明方法的接口,該接口可以是代理類賴以繼承方法的代理接口的超接口。

args - 包含傳入代理執行個體上方法調用的參數值的對象數組,如果接口方法不使用參數,則為 null。基本類型的參數被包裝在适當基本包裝器類(如 java.lang.Integer 或 java.lang.Boolean)的執行個體中。

傳回:

從代理執行個體的方法調用傳回的值。如果接口方法的聲明傳回類型是基本類型,則此方法傳回的值一定是相應基本包裝對象類的執行個體;否則,它一定是可配置設定到聲明傳回類型的類型。如果此方法傳回的值為 null

并且接口方法的傳回類型是基本類型,則代理執行個體上的方法調用将抛出 NullPointerException。否則,如果此方法傳回的值與上述接口方法的聲明傳回類型不相容,則代理執行個體上的方法調用将抛出 ClassCastException。

抛出:

Throwable - 從代理執行個體上的方法調用抛出的異常。該異常的類型必須可以配置設定到在接口方法的 throws 子句中聲明的任一異常類型或未經檢查的異常類型 java.lang.RuntimeException 或 java.lang.Error。如果此方法抛

出經過檢查的異常,該異常不可配置設定到在接口方法的 throws 子句中聲明的任一異常類型,代理執行個體的方法調用将抛出包含此方法曾抛出的異常的  

    2,其次是在代理類中要有Proxy類的支援,Proxy是專門用于完成代理的代理操作類,可以通過此類為一個或多個接口動态地生成實作類。proxy類提供的方法如下:

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 - 指派方法調用的調用處理程式

傳回:

一個帶有代理類的指定調用處理程式的代理執行個體,它由指定的類加載器定義,并實作指定的接口

抛出:

IllegalArgumentException - 如果違反傳遞到 getProxyClass 的參數上的任何限制

NullPointerException - 如果 interfaces 數組參數或其任何元素為 null,或如果調用處理程式 h 為 null

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,定義MyInvocationHandler類

  package proxy.test;

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;

public class MyInvocationHandler implements InvocationHandler{

    private Object obj;//真實主題

    public Object bind(Object obj){//綁定真實主題操作

     this.obj = obj;

     return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(),this);//取得代理類

    }

 @Override

 public Object invoke(Object proxy, Method method, Object[] args)//動态調用方法

   throws Throwable {

  // TODO Auto-generated method stub

  Object temp = method.invoke(this.obj, args);//調用方法,傳入真實主題和參數

  return temp;//傳回方法的傳回資訊

 }

}

2,定義接口

package proxy.test;

public interface Subject {  //定義Subject接口

     public String print(String word);//定義抽象方法print

}

3,定義真實主題實作類

package proxy.test;

public class RealSubject implements Subject{

 @Override

 public String print(String word) {

  // TODO Auto-generated method stub

  return "内容:"+word;

 }

}

4,測試動态代理

package proxy.test;

public class DynaProxyDemo {

 public static void main(String[] args) {

  // TODO Auto-generated method stub

       MyInvocationHandler handler = new MyInvocationHandler();

       Subject sub = (Subject)handler.bind(new RealSubject());

       String content = sub.print("hello");

       System.out.println(content);

 }

}

java教育訓練、android教育訓練期待與您交流!