Java Reflectionを使用すると、実行時にインターフェイスの動的実装を作成します。 これは、クラスjava.lang.reflect.Proxy
を使用して行います。 このクラスの名前は、これらの動的インターフェイス実装を動的プロキシと呼ぶ理由です。 動的プロキシは、データベース接続とトランザクション管理、単体テストのための動的モックオブジェクト、および他のAOPのようなメソッド傍受目的など、多
プロキシの作成
Proxy.newProxyInstance()
メソッドを使用して動的プロキシを作成します。 newProxyInstance()
メソッドは3つのパラメータを取ります:
- 動的プロキシクラスを”ロード”する
ClassLoader
。 - 実装するインターフェイスの配列。
- プロキシ上のすべてのメソッド呼び出しを転送する
InvocationHandler
。
以下に例を示します。
このコードを実行した後、proxy
変数にはMyInterface
インターフェイスの動的実装が含まれています。 プロキシへのすべての呼び出しは、一般的なInvocationHandler
インターフェイスのhandler
実装に転送されます。 InvocationHandlerのは、私は次のセクションで説明されています。
InvocationHandlerの
前述のように、InvocationHandler
実装をProxy.newProxyInstance()
メソッドに渡す必要があります。 動的プロキシへのすべてのメソッド呼び出しは、このInvocationHandler
実装に転送されます。 InvocationHandler
インターフェイスの外観は次のとおりです:
public interface InvocationHandler{ Object invoke(Object proxy, Method method, Object args) throws Throwable;}
実装例を次に示します:
public class MyInvocationHandler implements InvocationHandler{ public Object invoke(Object proxy, Method method, Object args) throws Throwable { //do something "dynamic" }}
invoke()
メソッドに渡されるproxy
パラメーターは、インターフェイスを実装する動的プロキシオブジェクトです。 ほとんどの場合、このオブジェクトは必要ありません。
invoke()
メソッドに渡されるMethod
オブジェクトは、動的プロキシが実装するインターフェイスで呼び出されるメソッドを表します。 Method
オブジェクトから、メソッド名、パラメータ型、戻り値の型などを取得できます。 詳細については、メソッドのテキストを参照してください。
Object args
配列には、実装されたインターフェイスのメソッドが呼び出されたときにプロキシに渡されるパラメータ値が含まれています。 注:実装されたインターフェイスのプリミティブ(int、longなど)は、オブジェクトの対応するもの(Integer、Longなど)にラップされます。).
既知の使用例
動的プロキシは、少なくとも次の目的で使用されることが知られています:
- データベース接続とトランザクション管理
- 単体テストのための動的モックオブジェクト
- DIコンテナのカスタムファクトリインターフェイスへの適応
- AOPライクなメソッド傍受
データベース接続とトランザクション管理
Springフレームワークには、トランザクションを開始してコミット/ロールバックできるトランザクションプロキシがあります。 これがどのように機能するかは、テキスト”高度な接続とトランザクションの境界と伝播”で詳しく説明されているので、簡単に説明します。 呼び出しシーケンスはこれに沿って何かになります:
単体テストのための動的モックオブジェクト
Butterflyテストツールは、動的プロキシを使用して、単体テストのための動的スタブ、モック、およびプロキシを実装します。 別のクラスB(実際にはインターフェイス)を使用するクラスAをテストするときは、実際のBではなくBのモック実装をAに渡すことができます。 Bのすべてのメソッド呼び出しが記録され、モックBが返す戻り値を設定できます。
さらに、Butterflyテストツールを使用すると、実際のBをモックBにラップすることができ、モック上のすべてのメソッド呼び出しが記録され、実際のBに転送されます。 DAOは違いを認識せず、モックはすべての呼び出しをデータベースに転送するため、DAOは通常どおりデータをデータベースに読み書きできます。 しかし、DAOが接続を適切に使用しているかどうか、たとえばconnection.close()
が呼び出されている(または呼び出されていない)かどうかをモックで確認できます。 これは通常、DAOの戻り値から判断することはできません。
DI Containerのカスタムファクトリインターフェイスへの適応
依存性注入コンテナButterfly Containerには、コンテナ全体をそれによって生成されたbeanに注入できる強力な機能があります。 しかし、コンテナインターフェイスに依存したくないので、コンテナはデザインのカスタムファクトリインターフェイスに適応することができます。 あなただけのインターフェイスが必要です。 実装なし。 したがって、ファクトリインタフェースとあなたのクラスは次のようになります:
public interface IMyFactory { Bean bean1(); Person person(); ...}
MyAction
クラスがコンテナによってコンストラクターに注入されたIMyFactory
インスタンスのメソッドを呼び出すと、メソッド呼び出しはIContainer.instance()
メソッドへの呼び出しに変換されます。 そうすれば、オブジェクトは、作成時に依存関係を挿入するのではなく、実行時にButterflyコンテナをファクトリとして使用できます。 そして、これは任意の蝶コンテナ固有のインターフェイス上の任意の依存関係を持たずに。
Aopのようなメソッドの傍受
Springフレームワークは、beanがいくつかのインターフェイスを実装していれば、特定のbeanへのメソッド呼び出しを傍受すること Springフレームワークは、beanを動的プロキシでラップします。 Beanへのすべての呼び出しは、プロキシによってインターセプトされます。 プロキシは、メソッド呼び出しをbeanラップに委譲する前、代わりに、または後に、他のオブジェクトの他のメソッドを呼び出すことを決定できます。