問題の歴史:
ダイナミックプロキシパターンはJava 1.3で登場し、AOP、ロギング、セキュリティ、手動でプロキシクラスを作成することなくコードを動的に生成するパターンを実装可能にしました。これはSpring、Hibernateおよび他のフレームワークで非常に人気が高まりました。
問題:
既存のインターフェイスに機能を追加したり、実行時にメソッドの動作を変更したりする必要がある場合がありますが、元のコードを変更することなく行う必要があります。例:メソッドの呼び出しごとのロギング、アクセス制御、プロファイリング。主な問題は、すべてのクラスに対してこれを「手動」で行うことができなく、特に多くのクラスがある場合や動的に生成される場合です。
解決策:
Javaは、java.lang.reflectパッケージに標準APIを提供し、Proxy.newProxyInstanceを使用して任意のインターフェイスのダイナミックプロキシオブジェクトを「その場で」作成できます。InvocationHandlerオブジェクトが実装され、各メソッド呼び出しを受け取り、独自のロジックを実行します。
コードの例:
import java.lang.reflect.*; interface Service { void serve(); } class ServiceImpl implements Service { public void serve() { System.out.println("Serving..."); } } class LoggingHandler implements InvocationHandler { private final Object target; public LoggingHandler(Object target) { this.target = target; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Calling " + method.getName()); return method.invoke(target, args); } } Service service = (Service) Proxy.newProxyInstance( Service.class.getClassLoader(), new Class[]{Service.class}, new LoggingHandler(new ServiceImpl())); service.serve();
主な特徴:
標準のダイナミックプロキシを使用して、インターフェイスを実装していない通常のクラスを「ラップ」できますか?
いいえ。ダイナミックプロキシはインターフェイスのみで機能します。クラスにはバイトコード操作が必要です(例えばCGLIB)。
インターフェイスにInvocationHandlerの戻り値と互換性のない返り値の型を持つメソッドが含まれている場合、何が起こりますか?
ClassCastExceptionがスローされるため、戻り値はインターフェイスの定義と厳密に互換性がある必要があります。
finalメソッドにダイナミックプロキシを使用できますか?
いいえ。プロキシはインターフェイスレベルでのみ機能し、finalメソッドは存在しません。finalメソッドは全くオーバーライドできません。
ネガティブケース
開発者はインターフェイスのないクラスにProxyを介して監査ロギングを実装しようとしました
長所:
短所:
ポジティブケース
契約をインターフェイスとして分離し、そのためにプロキシラッパーを作成しました
長所:
短所: