JDK動的プロキシとCGLibの違いは何ですか?


147

以下の場合、プロキシデザインパターン、違いは何であるJDKの動的プロキシやサードパーティの動的なコード生成APIのようなCGLIBは

両方のアプローチを使用することと、一方を他方より優先する必要がある場合の違いは何ですか?


3
ここでコードを取得してください:< gist.github.com/ksauzz/1563486 >。cglibでは、クラスプロキシとインターフェイスプロキシの両方を作成できます。SpringはデフォルトでCGlibを​​使用し、AspectJはJavaプロキシを使用します。これも読んでください:jnb.ociweb.com/jnb/jnbNov2005.html ;)
Subhadeep Ray

回答:


185

JDK動的プロキシーはインターフェースによってのみプロキシーを実行できます(そのため、ターゲット・クラスはインターフェースを実装する必要があります。インターフェースはプロキシー・クラスによっても実装されます)。

CGLIB(およびjavassist)は、サブクラス化によってプロキシを作成できます。このシナリオでは、プロキシはターゲットクラスのサブクラスになります。インターフェイスは必要ありません。

したがって、Java動的プロキシはプロキシできますpublic class Foo implements iFoo。CGLIBはプロキシできます。public class Foo

編集:

javassistとCGLIBはサブクラス化によってプロキシを使用するため、これに依存するフレームワークを使用するときにfinalメソッドを宣言したりクラスをfinalにしたりできないのはそのためです。これにより、これらのライブラリがクラスをサブクラス化してメソッドをオーバーライドすることができなくなります。


ありがとう.. !! しかし、場合によっては、あるコード(またはリンク)の例を示して、別のコードの使用法を説明できると助かります。
KDjava、2012年

1
JDKプロキシは、実際にはIFooのプロキシを停止していることに注意してください。それはかなり重要な違いです。また、cglibプロキシは完全なサブクラスです-それを活用してください!フィルターを使用して、気になるメソッドのみをプロキシし、生成されたクラスを直接使用します。
lscoughlin

9
また、CGLibサブクラスの作成には、正しい引数を使用して正しいコンストラクターを呼び出すことができるように、スーパークラスについて十分な知識が必要であることにも注意してください。コンストラクタを気にしないインターフェイスベースのプロキシとは異なります。これにより、CGLibプロキシでの作業がJDKプロキシよりも「自動」ではなくなります。もう1つの違いは、「スタック」コストです。JDKプロキシは常に呼び出しごとに追加のスタックフレームを発生させますが、CGLibは追加のスタックフレームにコストをかけない場合があります。これは、アプリが複雑になるほど関連性が高まります(スタックが大きいほど、メモリスレッドが消費するため)。
レイ

1
cglibはfinalメソッドをプロキシできませんが、例外gist.github.com/mhewedy/7345403cfa52e6f47563f8a204ec0e80
Muhammad Hewedy

はい、CGLIBは最終的なメソッドを単に無視します。
yashjain12yj

56

機能の違い

  • JDKプロキシを使用すると、サブクラス化中に任意のインターフェイスセットを実装できますObject。任意のインターフェイスメソッドは、プラスObject::hashCodeObject::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は現在活発に開発されていないことに注意してください。


2
春のドキュメントでは、後者のパフォーマンス上の利点を考慮して、cglibよりもJDKプロキシを優先するのはなぜですか?docs.spring.io/spring/docs/2.5.x/reference/...
P4ndaman

2
Cglibは外部依存であり、現在サポートされていません。サードパーティのソフトウェアに依存することは常にギャンブルであるため、可能な限り少数の人がそれに依存する場合に最適です。
Rafael Winterhalter

あなたのブログでは、「しかし、InvocationHandler#invokeメソッドに付属するプロキシオブジェクトのメソッドを呼び出すときは注意が必要です。このメソッドのすべての呼び出しは、同じInvocationHandlerでディスパッチされるため、無限ループになる可能性があります。 」どういう意味ですか?
Koray Tugay 2017年

プロキシオブジェクトのメソッドを呼び出すと、すべての呼び出しは呼び出しハンドラを介してルーティングされます。いずれかの呼び出しハンドラー呼び出しがオブジェクトの呼び出しに委譲すると、前述の再帰が発生します。
Rafael Winterhalter 2017年

こんにちはラファエル、あなたの答えとは無関係のメッセージです。5年前に行われた編集についてpingを送信しています。以下のようCGLIBは明らかにまだ2019年のコミットを持ち、任意の表示されません逮捕開発のreadmeのを、私はしましたあなたの文の削除タグの抜粋からを。言及すべきことがあれば、タグの説明や抜粋を自由に改善してください。
・クール

28

動的プロキシ:JDKリフレクションAPIを使用した実行時のインターフェースの動的実装。

例: Springは、次のようにトランザクションに動的プロキシを使用します。

ここに画像の説明を入力してください

生成されたプロキシはBeanの上に配置されます。Beanに国境を越えた動作を追加します。ここで、プロキシは、JDKリフレクションAPIを使用して実行時に動的に生成します。

アプリケーションが停止すると、プロキシは破棄され、ファイルシステムにはインターフェイスとBeanのみが存在します。


上記の例では、インターフェースがあります。しかし、ほとんどのインターフェイスの実装は最善ではありません。したがって、Beanはインターフェースを実装しません。その場合、継承を使用します。

ここに画像の説明を入力してください

このようなプロキシを生成するために、SpringはCGLibと呼ばれるサードパーティのライブラリを使用します。

CGLib(C ode G eneration Lib rary )はASMの上に構築されています。これは主にプロキシ拡張Beanの生成に使用され、プロキシメソッドにBeanの動作を追加します。

JDK動的プロキシおよびCGLibの例

春の参考文献


5

Springドキュメントから

Spring AOPは、JDK動的プロキシまたはCGLIBのいずれかを使用して、特定のターゲットオブジェクトのプロキシを作成します。(選択肢がある場合はいつでも、JDK動的プロキシーが推奨されます)。

プロキシされるターゲットオブジェクトが少なくとも1つのインターフェースを実装する場合、JDK動的プロキシが使用されます。ターゲットタイプによって実装されたすべてのインターフェースがプロキシされます。ターゲットオブジェクトがインターフェイスを実装していない場合は、CGLIBプロキシが作成されます。

CGLIBプロキシーの使用を強制したい場合(例えば、インターフェースによって実装されたものだけでなく、ターゲットオブジェクトに対して定義されたすべてのメソッドをプロキシーする場合)は、そうすることができます。ただし、考慮すべきいくつかの問題があります。

最終的なメソッドはオーバーライドできないため、アドバイスできません。

ダイナミックプロキシはJDKで利用可能ですが、クラスパスにはCGLIB 2バイナリが必要です。Springは、CGLIBが必要で、クラスパスにCGLIBライブラリクラスが見つからない場合、自動的に警告します。

プロキシオブジェクトのコンストラクタは2回呼び出されます。これは、プロキシされたオブジェクトごとにサブクラスが生成されるCGLIBプロキシモデルの自然な結果です。プロキシされるインスタンスごとに、2つのオブジェクトが作成されます。実際のプロキシされるオブジェクトと、アドバイスを実装するサブクラスのインスタンスです。この動作は、JDKプロキシを使用している場合は発生しません。通常、プロキシタイプのコンストラクタを2回呼び出すことは問題ではありません。通常、割り当てのみが行われ、実際のロジックはコンストラクタに実装されていないためです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.