「こんにちは、アミーゴ」

「こんにちは、リシさん」

「今日は、新しくて非常に興味深いトピックである動的プロキシについて説明します。 」

「Java には、特定のクラスの機能を変更する方法がいくつかあります…」

一つ目の方法は継承です。

「クラスの動作を変更する最も簡単な方法は、元の (基本) クラスを継承する新しいクラスを作成し、そのメソッドをオーバーライドすることです。次に、元のクラスを使用する代わりに、派生クラスを使用します。次に例を示します。」

Reader reader = new UserCustomReader();

2 番目の方法は、ラッパー クラスを使用することです。

" BufferedReaderは、このタイプのクラスの例です。まず、Reader を継承します。つまり、 Reader の代わりに使用できます。第 2 に、すべての呼び出しを元のReaderオブジェクトにリダイレクトします。このオブジェクトは BufferedReader オブジェクトのコンストラクターに渡す必要があります。。 例えば:"

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

3 番目の方法は、動的プロキシ (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メソッドが呼び出され、画面に「yes!」と表示されます。」

「そこで、 CustomInvocationHandlerクラスを宣言し、InvocationHandlerインターフェイスとそのinvokeメソッドを実装しました。invoke メソッドが呼び出されると、「はい!」と表示されます。次に、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);
 }
}

「この例には 2 つの特別な特徴があります。」

「まず、«original» Readerオブジェクトがコンストラクターに渡され、それへの参照が CustomInvocationHandler 内に保存されます

「2 番目に、これと同じメソッドを invoke メソッドで再度呼び出しますが、今回は «original» オブジェクトに対して呼び出します。」

「ああ。つまり、この最後の行は同じメソッドを呼び出しますが、元のオブジェクトに対して次のようになります。」

return method.invoke(readerOriginal, args);

「はい。」

「それが非常に明白だとは言いませんが、それでも理解できることです。あるいは、そのように思えます。」

「素晴らしいですね。それではもう 1 つ。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 のものですね。」

「はい。」

「なるほど。そうですね、必要に応じて、原始的で非常に単純なプロキシ オブジェクトを作成できると思います。」

「それではディエゴに連絡してください。」