Java的代理機制

pedro7發表於2022-02-06

Java的代理機制

使用代理 Proxzy 可以在執行時建立一組給定介面的新類,這種功能只有在編譯時無法確定需要實現哪種介面時才需要使用。

1. 使用代理的時機

假如有一個表示介面的 Class 物件,它的確切型別在編譯時無法得知。由於沒有實現類而只有一個介面,反射和newInstance語句是無法例項化這個 Class 物件的,我們需要在程式處於執行狀態時定義一個新類。

代理類可以在執行時建立全新的類,這樣的代理類可以實現指定的介面,它具有下列方法:

  • 指定介面所需要的全部方法。
  • Object 類中的全部方法,例如toString()equals()等。

在代理機制中,不能再允許時定義這些方法的新程式碼,而是要提供一個呼叫處理器 InvocationHandler呼叫處理器是實現了InvocationHandler介面的類物件。在這個介面中只有一個方法:

Object invoke(Object proxy, Method method, Object[] args)

無論何時呼叫代理物件的方法,invoke()方法都被呼叫,並向其傳遞 Method 物件和原始的呼叫引數,呼叫處理器必須給出處理呼叫的方式。

2. 建立代理物件

建立代理物件要使用 Proxy 類的newProxyInstance方法,這個方法有三個引數:

  • 一個類載入器。可以使用不同的類載入器,用 null 表示使用預設的類載入器。
  • 一個 Class 物件陣列。每個元素都是需要實現的介面。
  • 一個呼叫處理器。

下面給出一個示例程式,使用代理和呼叫處理跟蹤方法呼叫:

// 呼叫處理器
class TraceHandler implements InvocationHandler{
    private Object target;

    // 建構函式
    public TraceHandler(Object t){
        target = t;
    }

    // invoke方法
    public Object invoke(Objcet proxy, Method m, Object[] args) throws Throwable {
        // print method name and parameters
        ...
        // invoke actual method
        return m.invoke(target, args);
    }
}

下面的程式碼,我們用於跟蹤方法呼叫的代理物件:

Object value = ...;
// 構造呼叫處理器
InvocationHandler handler = new TraceHandler(value);
// 構造代理物件
Class[] interfaces = new Class[](Comparable.class);
Object proxy = Proxy.newProxyInstance(null, interfaces, handler);

我們再用 proxy 任何方法時,都會呼叫invoke()方法,列印出方法的名字和引數,再用value物件呼叫它。

3. 代理類的特性

代理類有下面這樣一些特性:

  • 所有的代理類都擴充套件於 Proxy 類。一個代理類只有一個例項域:呼叫處理器。
  • 所需的任何附加資料儲存在呼叫處理器中,例如代理 Comparable 物件時,在TraceHandler 中包裝了實際的物件target
  • 所有的代理類都覆蓋了 Object 類中的方法toString()equals()hashCode()。如果所有的代理方法一樣,這些方法僅僅呼叫了呼叫處理器的invoke()。Object 類中的其他方法沒有被重新定義。
  • 對於特定的類載入器和一組預設的介面,最多隻能有一個代理類。也就是說如果用同樣的引數重複呼叫兩次newProxyInstance()方法,那麼只能得到一個類的兩個物件。
  • 代理類一定是 public、final 。如果代理類實現的所有介面都是 public 的,代理類就不屬於某個特定的包。否則所有非公有的介面必須屬於同一個包,代理類也屬於這個包。
  • 可以用 Proxy 類的isProxyClass()方法檢測一個特定的 Class 物件是否是一個代理類。

相關文章