web-dev-qa-db-ja.com

cglibに代わるものはありますか?

好奇心から、ランタイム用の(安定した)オープンソースプロジェクトはありますかJava cglib以外のコード生成?なぜそれらを使用する必要があるのですか?

57
Mauli

[〜#〜] asm [〜#〜]Java-asm

CGLIBおよび他のほとんどすべてのライブラリは、それ自体が非常に低いレベルで動作するASMの上に構築されています。バイトコードと [〜#〜] jvms [〜#〜] を適切に使用するには、バイトコードと少し理解する必要があるので、これはほとんどの人にとってショーストッパーです。しかし、ASMの習得は非常に興味深いものです。ただし、greatASM 4ガイド がありますが、APIの一部では、javadocドキュメントが非常に簡潔になる場合があります存在する場合でも、改善されます。新しいバージョンをサポートするために、JVMのバージョンに厳密に従っています。

ただし、完全な制御が必要な場合は、ASMが最適です。

このプロジェクトは定期的に更新されます。この編集時点で、バージョン5.0.4は2015年5月15日にリリースされました。

バイトバディバイトバディ

Byte Buddyはかなり新しいライブラリですが、CGLIBまたはJavassistが提供するすべての機能を提供します。バイトバディはバイトコードレベルまで完全にカスタマイズでき、非常に読みやすいコードを可能にする表現力のあるドメイン固有の言語が付属しています。

  • デフォルトのメソッドに関するいくつかのオペコードのJava 8セマンティック変更を含む)を含む、すべてのJVMバイトコードバージョンをサポートしています。
  • ByteBuddyは他のライブラリが持っている欠点に悩まされていないようです
  • 高度に構成可能
  • かなり速い( ベンチマークコード
  • 型安全なFluent API
  • タイプセーフコールバック

    Javassistのアドバイスまたはカスタムインストルメンテーションコードは、プレーンStringのコードに基づいているため、このコード内で型チェックとデバッグを行うことはできませんが、ByteBuddyでは、純粋なJava型チェックを行い、デバッグを許可します。

  • アノテーションドリブン(フレキシブル)

    ユーザーコールバックは、コールバックで必要なパラメーターを受け取ることができる注釈を使用して構成できます。

  • エージェントとして利用可能

    Niftyエージェントビルダーを使用すると、ByteBuddyを純粋なエージェントまたは接続エージェントとして使用できます。それは別の種類を可能にします

  • 非常によく文書化されている
  • たくさんの例
  • クリーンなコード、最大94%のテストカバレッジ
  • Android DEXのサポート

主な欠点は、おそらくAPIは初心者には少し冗長ですが、プロキシ生成DSLとして形成されたオプトインAPIとして設計されています。魔法や疑わしいデフォルトはありません。バイトコードを操作するとき、それはおそらく最も安全で最も合理的な選択です。また、複数の例と大きなチュートリアルがあるため、これは実際の問題ではありません。

2015年10月、このプロジェクトは Oracle Duke's Choice賞 を受賞しました。この時点で 1.0.0マイルストーン に達しました。これはかなりの成果です。

バージョン2.1.0では mockitoCGLIB by Byte Buddy に置き換わっています。

Javassistjavassist

Javassistのjavadocは、CGLIBのjavadocよりもはるかに優れています。クラスエンジニアリングAPIは問題ありませんが、Javassistも完璧ではありません。特に、CGLIBのProxyFactoryに相当するEnhancerにもいくつかの欠点があります。

  • Bridgeメソッドは完全にはサポートされていません(つまり、共変の戻り値の型に対して生成されるメソッド)
  • ClassloaderProviderは代わりに静的フィールドであり、同じクラスローダー内のすべてのインスタンスに適用されます
  • カスタムの命名は歓迎されたかもしれません(署名されたjarのチェック付き)
  • 拡張ポイントはなく、関心のあるほとんどすべてのメソッドはプライベートです。これは、一部の動作を変更する場合に面倒です
  • Javassistはクラスのアノテーション属性をサポートしていますが、ProxyFactoryではサポートされていません。

アスペクト指向の側では、プロキシにコードを挿入できますが、Javassistのこのアプローチは制限されており、少しエラーが発生しやすくなります。

  • アスペクトコードはプレーンなJava=compiledopcodesでコンパイルされた文字列)で記述されています
  • タイプチェックなし
  • ジェネリックなし
  • ラムダなし
  • 自動(非)ボクシングなし

また、JavassistはCglibよりも遅いことが認識されています。これは主に、CGLIBのようにロードされたクラスを読み取るのではなく、クラスファイルを読み取るというアプローチによるものです。そして implementation 自体は公平であると読むのは難しいです。 Javassistコードを変更する必要がある場合、何かを壊す可能性がたくさんあります。

Javassistも非アクティブに苦しんでおり、コミュニティからの定期的なコミットとプルリクエストを示しているため、 2013年頃のgithub への移行は有用であることが証明されているようです。

これらの制限は、バージョン3.17.1にも残っています。バージョンはバージョン3.20.0に引き上げられましたが、JavassistはJava 8のサポートに問題がある可能性があります。

JiteScript

JiteScriptは、ASMのDSLを適切に構成する新しい部分のように見えます。これは、最新のASMリリース(4.0)に基づいています。コードはきれいに見えます。

しかし、プロジェクトはまだ初期の段階にあるため、API /動作が変更される可能性があり、さらにドキュメントは悲惨です。放棄しないと更新が不足します。

Proxettajodd

これはかなり新しいツールですが、はるかに優れたhumanAPIを提供します。サブクラスプロキシ(cglibアプローチ)、ウィービング、デリゲートなど、さまざまなタイプのプロキシを使用できます。

これはかなりまれですが、うまく機能する場合は情報がありません。バイトコードを処理する場合、対処すべき非常に多くのケースがあります。

AspectJaspectj

AspectJはアスペクト指向プログラミング(のみ)のための非常に強力なツールです。 AspectJは、バイトコードを操作してその目的を達成し、ユーザーがそれを使用して目的を達成できるようにします。ただし、これにはコンパイル時の操作が必要です。バージョン 2.54.1.x 以降、エージェントを介したロード時のスプリングオファーウィービング。

[〜#〜] cglib [〜#〜]cglib

その質問以降に更新されたCGLIBについての言葉

CGLIBは非常に高速です。これは、CGLIBが現在(2014年〜2015年)まで他のどの方法よりもほとんど機能していないという事実とともに、それがまだ存在する主な理由の1つです。

一般的に言えば、実行時にクラスの再書き込みを可能にするライブラリは、対応するクラスが再書き込みされる前に型をロードしないようにする必要があります。したがって、JavaリフレクションAPIを使用することはできません。リフレクションで使用されるすべてのタイプをロードする必要があります。代わりに、IOを介してクラスファイルを読み取る必要があります。 =(これはパフォーマンスブレーカーです)これにより、たとえばJavassistまたはProxettaは、リフレクションAPIを介してメソッドを単に読み取り、オーバーライドするCglibよりも大幅に遅くなります。

ただし、CGLIBは現在活発に開発されていません。最近のリリースがありましたが、それらの変更は多くの人にとって重要ではないと見なされ、CGLIBがいくつかの 重大なバグ を導入したため、ほとんどの人はバージョン3に更新しませんでした。 バージョン3.1は、バージョン3.0の多くの問題を修正しました(バージョン4.0.3 Springフレームワークが再パッケージ化 バージョン3.1 以降)。

また、CGLIBのソースコードは 品質が悪い なので、CGLIBプロジェクトに新しい開発者が参加することはありません。 CGLIBの活発さの印象については、彼らの メーリングリスト を参照してください。

guiceメーリングリストの提案 に続いて、CGLIBが github で利用できるようになり、コミュニティがプロジェクトをよりよく支援できるようになりました(複数のコミットとプルリクエスト、ci、更新されたmaven)、まだほとんどの懸念が残っています。

現在、バージョン3.2.0に取り組んでおり、Java 8に注力していますが、これまでのところ、Java 8のサポートが必要なユーザーはビルド時にトリックを使用しますが、進行は非常に遅いです。

また、CGLIBはPermGenのメモリリークに悩まされていることがまだ知られています。しかし、他のプロジェクトでは、長年にわたって戦闘テストが行​​われていない可能性があります。

コンパイル時の注釈処理注釈処理

これはもちろんランタイムではありませんが、エコシステムの重要な部分であり、ほとんどのコード生成の使用はランタイムの作成を必要としません。

これは、Java 5で始まり、注釈を処理するための個別のコマンドラインツールに付属していました:apt、およびJava 6から始まりますJavaコンパイラに統合されています。

場合によっては、明示的にプロセッサを渡す必要がありましたが、ServiceLoaderアプローチを使用します(このファイルを追加するだけですMETA-INF/services/javax.annotation.processing.Processor to jar)コンパイラは注釈プロセッサを自動的に検出できます。

このコード生成のアプローチには欠点もあります。バイトコードではなく、Java言語の理解に多くの作業と理解が必要です。このAPIは少し扱いに​​くく、コンパイラのプラグインなので、このコードを最も復元力があり、ユーザーフレンドリーなエラーメッセージにするために細心の注意を払ってください。

ここでの最大の利点は、実行時に別の依存関係を回避できることです。permgenのメモリリークを回避できます。そして、生成されたコードを完全に制御できます。

結論

2002 では、CGLIBはバイトコードを簡単に操作するための新しい標準を定義しました。私たちが今日持っている多くのツールと方法論(CI、カバレッジ、TDDなど)は、その時点では利用できないか、成熟していませんでした。 CGLIBは10年以上にわたって関連性を持つことができました。それはかなりまともな成果です。オペコードを直接操作するよりも高速で使いやすいAPIを備えていました。

それはコード生成に関する新しい標準を定義しましたが、今日では環境と要件が変更されたため、もはや標準ではなく、標準と目標があります。

JVMが変更され、最近および将来的に変更されるJava(7/8/9/10)バージョン(invokedynamic、デフォルトのメソッド、値タイプなど)。ASMは定期的にAPIと内部をアップグレードしましたこれらの変更に従いますが、CGLIBや他のユーザーはまだそれらを使用していません。

注釈処理は魅力的になっていますが、実行時の生成ほど柔軟ではありません。

2015年現在、Byte Buddy一方、シーンではかなり新しい—最も魅力的なsellingはランタイム生成を指します。まともな更新率、そして作者はJavaバイトコードの内部についての深い知識を持っています。

103
Brice

とにかくcglibで使用されていると思います [〜#〜] asm [〜#〜] 。それは低レベルですが、ドキュメントは素晴らしいであり、慣れると飛行します。

2番目の質問に答えるには、リフレクションプロキシと動的プロキシが少し混ざり合っているように感じ始め、堅実なソリューションが必要な場合に、コード生成を使用する必要があります。過去には、Eclipseのビルドプロセスにコード生成ステップを追加して、事実上、あらゆるもののコンパイル時間レポートを提供していました。

10
CurtainDog

Javassist

プロキシを作成する必要がある場合は、 commons-proxy を見てください。これは、CGLIBとJavassitの両方を使用します。

10
Bozho

Cglibの代わりに Javassist を使用する方が理にかなっていると思います。例えば。 javasistは、cglibとは異なり、署名されたjarと完全に連携します。その上、Hibernateプロジェクトなどの壮大なもの Javassistのためにcglibの使用を中止することを決定

4
FoxyBOA

CGLIBは、AOPとORMの時代に10年以上前に設計および実装されました。現在、それを使用する理由はなく、このライブラリを維持していません(レガシーアプリケーションのバグ修正を除く)。実際、私が今まで見たすべてのCGLIBユースケースは、現代のプログラミングにおけるアンチパターンです。 JVMのスクリプト言語を介して同じ機能を実装することは簡単です。グルーヴィー。

0
jbaliuka