問題の歴史:
プロキシオブジェクトとプロキシパターンは、Javaで置き換え用のオブジェクトを作成し、実際のオブジェクトへのアクセスを制御したり、その振る舞いを変更したり、クロスカット機能(セキュリティ、ロギング、メトリクス)を埋め込むために登場しました。Java 1.3以降、java.lang.reflectパッケージで標準のダイナミックプロキシのサポートが追加され、後にCGLIBやByteBuddyといった人気のあるライブラリが登場しました。
問題:
既存のクラスのロジックを直接変更することは、いつも可能であるとは限りません。しばしば、オブジェクトのソースコードを変更せずに(たとえば、ロギング、キャッシング、トランザクションなど)の振る舞いを透明に追加することが必要ですが、これは標準の継承手法では不可能です。
解決策:
プロキシオブジェクトは、静的に(手動のサブクラス化によって)または動的に(リフレクションやバイトコードメカニズムを通じて)実装できます。ダイナミックプロキシは、必要なインターフェースを実装するオブジェクトをその場で作成し、invoke()メソッド内で実際のオブジェクトへの呼び出しを委任することを可能にし、外部の振る舞いを埋め込む柔軟性を提供します。
コードの例:
import java.lang.reflect.*; interface Service { void doWork(); } class RealService implements Service { public void doWork() { System.out.println("Doing work!"); } } class LoggingInvocationHandler implements InvocationHandler { private final Object target; public LoggingInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Calling: " + method.getName()); return method.invoke(target, args); } } Service real = new RealService(); Service proxy = (Service) Proxy.newProxyInstance( real.getClass().getClassLoader(), new Class[] {Service.class}, new LoggingInvocationHandler(real)); proxy.doWork(); // ログに呼び出しが記録される
主な特徴:
標準のJavaのダイナミックプロキシは、インターフェースを実装していないクラスで機能しますか?
いいえ。標準のJavaプロキシはインターフェースでのみ動作します。インターフェースを実装していないクラス(たとえば、通常のクラスをプロキシしたい場合)には、サードパーティの手段が必要です(例:CGLIB、ByteBuddy)。
プライベートメソッドを持つインターフェースのプロキシは作成できますか?
いいえ。Javaのインターフェースは、プロキシする必要のあるプライベートメソッドを含めることができません。ダイナミックプロキシはインターフェースのパブリックメソッドを実装します。Java 9以降、プライベートデフォルトメソッドが登場しましたが、これらはプロキシを通じてアクセスできません。
InvocationHandlerとMethodInterceptor(CGLIB)の違いは何ですか?
InvocationHandlerは、ダイナミックプロキシに使用されるJDKの標準インターフェースです。インターフェースのメソッドの呼び出しを受け取ります。MethodInterceptorは、CGLIBのインターフェースであり、動的継承を通じて任意のクラスのメソッド呼び出しをインターセプトすることを可能にします。適用性とレベルは異なります:JDKはインターフェースのみに、CGLIBは任意のクラスに対応しています。
エンジニアがすべてのサービスのメソッドで手動でロギングのコードを繰り返している。新しいメソッドを追加するたびに、論理を重複して記述し、必要な呼び出しを追加し忘れることが多い。
長所:
短所:
プロジェクトでダイナミックプロキシを介してAOPが導入され、ロギングとトランザクション管理が中央集権的に実装されたラッパーによって実現されます。
長所:
短所: