“嗨,阿米戈。”

“你好,瑞希。”

“今天我將向您解釋一個非常有趣的新話題:動態代理”

“Java 有幾種方法可以改變特定類的功能……”

“第一個方法,傳承。”

“更改類行為的最簡單方法是創建一個繼承原始(基)類的新類,並覆蓋其方法。然後,使用派生類而不是原始類。例如:”

Reader reader = new UserCustomReader();

“第二種方法是使用包裝類。”

" BufferedReader是這類類的一個例子。首先,它繼承了Reader。換句話說,它可以代替 Reader 使用。其次,它將所有調用重定向到原始Reader對象,必須傳遞給 BufferedReader 對象的構造函數。 例如:”

Reader readerOriginal = new UserCustomReader();
Reader reader = new BufferedReader(readerOriginal);

“第三種方法是創建一個動態代理(Proxy)。”

“Java 中有一個特殊的類 (java.lang.reflect.Proxy),它實際上允許您在程序執行期間(動態地)構造一個對象,而無需為其創建單獨的類。”

“這很容易做到:”

Reader reader = (Reader)Proxy.newProxyInstance();

“這對我來說已經是新鮮事了!”

“但是當然,我們不需要沒有方法的對象。我們需要對像有方法,我們需要它們來做我們想做的事情。Java為此使用了一個特殊的接口,稱為 InvocationHandler ,它可以攔截所有方法調用與代理對象相關聯。只能使用接口創建代理對象。”

Invoke – 是主要任務是簡單地調用某些方法的方法或類的標準名稱。”

Handler – 是處理某些事件的類的標準名稱。例如,處理鼠標點擊的類將稱為 MouseClickHandler 等。”

“InvocationHandler 接口有一個調用方法,所有對代理對象的調用都指向該方法。例如:”

代碼
Reader reader = (Reader)Proxy.newProxyInstance(new CustomInvocationHandler());
reader.close();
class CustomInvocationHandler implements InvocationHandler
{
 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
 {
  System.out.println("yes!");
  return null;
 }
}

“調用reader.close ()方法時,會調用invoke方法,屏幕會顯示‘是!’ ”

”所以,我們聲明了一個CustomInvocationHandler類,並實現了InvocationHandler接口和它的invoke方法,當invoke方法被調用時,顯示'yes!'。然後我們創建了一個CustomInvocationHandler對象,在創建時傳遞給newProxyInstance方法一個代理對象。”

“是的,都沒錯。”

“這是一個非常強大的工具。通常,創建這些代理是為了模擬在另一台計算機上物理運行的程序中的對象。 或者控制訪問。”

“您可以在這個方法中檢查當前用戶的權限、處理錯誤、記錄錯誤等等。”

“這是一個示例,其中 invoke 方法還調用原始對象的方法:”

代碼
Reader original = new UserCustomReader();

Reader reader = (Reader)Proxy.newProxyInstance(new CustomInvocationHandler(original));
reader.close();
class CustomInvocationHandler implements InvocationHandler
{
 private Reader readerOriginal;

 CustomInvocationHandler(Reader readerOriginal)
 {
  this.readerOriginal = readerOriginal;
 }

 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
 {
  if (method.getName().equals("close"))
  {
   System.out.println("Reader closed!");
  }

  // This calls the readerOriginal object's close method.
  // The method's name and a description of its parameters are stored in the method variable.
  return method.invoke(readerOriginal, args);
 }
}

“這個例子有兩個特點。”

“首先,將 «原始» Reader對像傳遞給構造函數,並將對它的引用保存在 CustomInvocationHandler 中

“其次,我們在 invoke 方法中再次調用相同的方法,但這次是在 «original» 對像上。”

“啊。換句話說,這最後一行調用了相同的方法,但在原始對像上:”

return method.invoke(readerOriginal, args);

“是的。”

“我不會說它非常明顯,但它仍然是可以理解的。或者看起來是這樣。”

“太好了。然後還有一件事。在 newProxyInstance 方法中,您需要傳遞更多一些內務信息來創建代理對象。但是由於我們沒有創建巨大的代理對象,因此可以很容易地從原始類本身獲取此信息。 “

“這是另一個例子:”

代碼
Reader original = new UserCustomReader();

ClassLoader classLoader = original.getClass().getClassLoader();
Class<?>[] interfaces = original.getClass().getInterfaces();
CustomInvocationHandler invocationHandler = new CustomInvocationHandler(original);

Reader reader = (Reader)Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
class CustomInvocationHandler implements InvocationHandler
{
 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
 {
  return null;
 }
}

“啊。ClassLoader 和接口列表。這是來自 Reflection 的東西,不是嗎?”

“是的。”

“我明白了。好吧,我想如果我需要的話,我可以創建一個原始的、超級簡單的代理對象。”

“那你去看看迪亞哥。”