以下の場合、プロキシデザインパターン、違いは何であるJDKの動的プロキシやサードパーティの動的なコード生成APIのようなCGLIBは?
両方のアプローチを使用することと、一方を他方より優先する必要がある場合の違いは何ですか?
以下の場合、プロキシデザインパターン、違いは何であるJDKの動的プロキシやサードパーティの動的なコード生成APIのようなCGLIBは?
両方のアプローチを使用することと、一方を他方より優先する必要がある場合の違いは何ですか?
回答:
JDK動的プロキシーはインターフェースによってのみプロキシーを実行できます(そのため、ターゲット・クラスはインターフェースを実装する必要があります。インターフェースはプロキシー・クラスによっても実装されます)。
CGLIB(およびjavassist)は、サブクラス化によってプロキシを作成できます。このシナリオでは、プロキシはターゲットクラスのサブクラスになります。インターフェイスは必要ありません。
したがって、Java動的プロキシはプロキシできますpublic class Foo implements iFoo
。CGLIBはプロキシできます。public class Foo
編集:
javassistとCGLIBはサブクラス化によってプロキシを使用するため、これに依存するフレームワークを使用するときにfinalメソッドを宣言したりクラスをfinalにしたりできないのはそのためです。これにより、これらのライブラリがクラスをサブクラス化してメソッドをオーバーライドすることができなくなります。
機能の違い
JDKプロキシを使用すると、サブクラス化中に任意のインターフェイスセットを実装できますObject
。任意のインターフェイスメソッドは、プラスObject::hashCode
、Object::equals
およびObject::toString
その後に転送されますInvocationHandler
。さらに、標準ライブラリインターフェイスjava.lang.reflect.Proxy
が実装されています。
cglibを使用すると、非最終クラスをサブクラス化しながら、任意のインターフェイスセットを実装できます。また、メソッドはオプションでオーバーライドできます。つまり、すべての非抽象メソッドをインターセプトする必要があるわけではありません。さらに、メソッドの実装にはさまざまな方法があります。InvocationHandler
クラス(別のパッケージ内)も提供しますが、たとえばのようなより高度なインターセプターを使用してスーパーメソッドを呼び出すこともできますMethodInterceptor
。さらに、cglibはのような特別なインターセプトによってパフォーマンスを向上させることができFixedValue
ます。cglibのさまざまなインターセプターの概要を書いたことがあります。
パフォーマンスの違い
JDKプロキシは、1つのインターセプトディスパッチャーであるで単純に実装されていInvocationHandler
ます。これには、常にインライン化できるとは限らない実装への仮想メソッドのディスパッチが必要です。Cglibでは、パフォーマンスを向上させることができる特殊なバイトコードを作成できます。18のスタブメソッドを使用してインターフェイスを実装するためのいくつかの比較を次に示します。
cglib JDK proxy
creation 804.000 (1.899) 973.650 (1.624)
invocation 0.002 (0.000) 0.005 (0.000)
時間はナノ秒単位で表記され、標準偏差は中括弧で囲まれています。ベンチマークの詳細については、Byte Buddyのチュートリアルをご覧ください。ByteBuddyは、cglibに代わる最新のツールです。また、cglibは現在活発に開発されていないことに注意してください。
動的プロキシ:JDKリフレクションAPIを使用した実行時のインターフェースの動的実装。
例: Springは、次のようにトランザクションに動的プロキシを使用します。
生成されたプロキシはBeanの上に配置されます。Beanに国境を越えた動作を追加します。ここで、プロキシは、JDKリフレクションAPIを使用して実行時に動的に生成します。
アプリケーションが停止すると、プロキシは破棄され、ファイルシステムにはインターフェイスとBeanのみが存在します。
上記の例では、インターフェースがあります。しかし、ほとんどのインターフェイスの実装は最善ではありません。したがって、Beanはインターフェースを実装しません。その場合、継承を使用します。
このようなプロキシを生成するために、SpringはCGLibと呼ばれるサードパーティのライブラリを使用します。
CGLib(C ode G eneration Lib rary )はASMの上に構築されています。これは主にプロキシ拡張Beanの生成に使用され、プロキシメソッドにBeanの動作を追加します。
Spring AOPは、JDK動的プロキシまたはCGLIBのいずれかを使用して、特定のターゲットオブジェクトのプロキシを作成します。(選択肢がある場合はいつでも、JDK動的プロキシーが推奨されます)。
プロキシされるターゲットオブジェクトが少なくとも1つのインターフェースを実装する場合、JDK動的プロキシが使用されます。ターゲットタイプによって実装されたすべてのインターフェースがプロキシされます。ターゲットオブジェクトがインターフェイスを実装していない場合は、CGLIBプロキシが作成されます。
CGLIBプロキシーの使用を強制したい場合(例えば、インターフェースによって実装されたものだけでなく、ターゲットオブジェクトに対して定義されたすべてのメソッドをプロキシーする場合)は、そうすることができます。ただし、考慮すべきいくつかの問題があります。
最終的なメソッドはオーバーライドできないため、アドバイスできません。
ダイナミックプロキシはJDKで利用可能ですが、クラスパスにはCGLIB 2バイナリが必要です。Springは、CGLIBが必要で、クラスパスにCGLIBライブラリクラスが見つからない場合、自動的に警告します。
プロキシオブジェクトのコンストラクタは2回呼び出されます。これは、プロキシされたオブジェクトごとにサブクラスが生成されるCGLIBプロキシモデルの自然な結果です。プロキシされるインスタンスごとに、2つのオブジェクトが作成されます。実際のプロキシされるオブジェクトと、アドバイスを実装するサブクラスのインスタンスです。この動作は、JDKプロキシを使用している場合は発生しません。通常、プロキシタイプのコンストラクタを2回呼び出すことは問題ではありません。通常、割り当てのみが行われ、実際のロジックはコンストラクタに実装されていないためです。