Byte Buddyは、Javaアプリケーションのランタイム中にJavaクラスを作成および変更するためのコード生成および操作ライブラリであり、コンパイラの助けなしです。 Javaクラスライブラリを搭載したコード生成ユーティリティ以外に、BYTE BUDDYは任意のクラスの作成を許可し、ランタイムプロキシを作成するためのインターフェイスの実装に限定されません。さらに、Byte Buddyは、Javaエージェントまたはビルド中に、手動でクラスを変更するための便利なAPIを提供します。
Byte Buddyを使用するには、Java Byteコードまたはクラスファイル形式を理解する必要はありません。対照的に、Byte BuddyのAPIは、誰にとっても簡潔で理解しやすいコードを目指しています。それにもかかわらず、Byte Buddyは、カスタムバイトコードを定義する可能性に完全にカスタマイズ可能なままです。さらに、APIは可能な限り侵入していないように設計されており、その結果、Byte Buddyはそれによって作成されたクラスに痕跡を残しません。このため、生成されたクラスは、クラスパスにバイトバディを必要とせずに存在できます。この機能のため、Byte Buddyのマスコットは幽霊に選ばれました。
Byte BuddyはJava 5で書かれていますが、Javaバージョンのクラスの生成をサポートしています。 Byte Buddyは軽量のライブラリであり、Java Byte Code Parser Library ASMのVisitor APIにのみ依存します。
一目で、ランタイムコード生成は、避けられるべきある種の黒い魔法のように見えることがあり、ランタイム中にコードを明示的に生成するアプリケーションを書く開発者はほとんどいません。ただし、この画像は、コンパイル時に不明な任意のコードとタイプと対話する必要があるライブラリを作成すると変化します。これに関連して、ライブラリの実装者は、多くの場合、ユーザーがライブラリ専用のインターフェイスを実装するように要求するか、ユーザーのタイプがライブラリに最初に知られているときに実行時にコードを生成することを選択する必要があります。たとえば、 SpringやHibernateなどの多くの既知のライブラリは、普通のJavaオブジェクトを使用するという用語でユーザーの間で人気のある後者のアプローチを選択します。その結果、コード生成はJavaスペースのユビキタスな概念になりました。 Byte Buddyは、コード生成に依存しているものにより優れたツールを提供するために、Javaタイプのランタイム作成を革新する試みです。
2015年10月、Byte BuddyはOracleによるデュークの選択賞で区別されました。この賞は、「 Java Technologyにおける膨大な量のイノベーション」でByte Buddyを高く評価しています。私たちはこの賞を受賞したことを非常に光栄に思っており、すべてのユーザーと他のすべてのユーザーに感謝したいと思います。本当に感謝しています!
Byte Buddyは、生産品質で優れたパフォーマンスを提供します。それは安定しており、モッキート、冬眠、ジャクソン、Googleのバゼルビルドシステムなどの著名なフレームワークとツールによって使用されています。 Byte Buddyは、多数の商用製品によっても大きな結果をもたらします。現在、年に7500万回以上ダウンロードされています。
Byte BuddyでHello Worldを言うのは、それが得られるほど簡単です。 Javaクラスの作成は、新しいタイプを作成するための構成を表すByteBuddy
クラスのインスタンスから始まります。
Class <?> dynamicType = new ByteBuddy ()
. subclass ( Object . class )
. method ( ElementMatchers . named ( "toString" ))
. intercept ( FixedValue . value ( "Hello World!" ))
. make ()
. load ( getClass (). getClassLoader ())
. getLoaded ();
assertThat ( dynamicType . newInstance (). toString (), is ( "Hello World!" ));
上記の例で使用されるデフォルトのByteBuddy
構成は、処理Java仮想マシンによって理解されるクラスファイル形式の最新バージョンにJavaクラスを作成します。例のコードから明白になっているように、作成されたタイプはObject
クラスを拡張し、 Hello World!
の固定値を返すようなtoString
メソッドをオーバーライドします! 。オーバーライドされる方法は、いわゆるElementMatcher
によって識別されます。上記の例では、正確な名前でメソッドを識別する定義済みの要素マッチャーnamed(String)
が使用されています。 Byte Buddyには、 ElementMatchers
クラスで収集され、簡単に構成できる多数の事前定義されたテストされたマッチャーが付属しています。ただし、カスタムマッチャーの作成は、(機能的な) ElementMatcher
インターフェイスを実装するのと同じくらい簡単です。
toString
メソッドを実装するために、 FixedValue
クラスは、オーバーライドされた方法の一定の返品値を定義します。一定の値を定義することは、バイトバディと一緒に出荷する多くのメソッドインターセプターの例の1つにすぎません。ただし、 Implementation
インターフェイスを実装することにより、メソッドはカスタムバイトコードで定義することさえできます。
最後に、説明されているJavaクラスが作成され、Java仮想マシンにロードされます。この目的のために、ターゲットクラスローダーが必要です。最終的には、作成されたクラスのインスタンスでtoString
メソッドを呼び出し、予想される定数値を表すリターン値を見つけることにより、結果を納得させることができます。
もちろん、 Hello Worldの例は、コード生成ライブラリの品質を評価するにはあまりにも単純なユースケースです。実際には、このようなライブラリのユーザーは、たとえば、Javaプログラムの実行パスにフックを導入するなど、より複雑な操作を実行したいと考えています。ただし、BYTE Buddyを使用すると、同様に簡単です。次の例は、メソッド呼び出しを傍受する方法の味を示しています。
Byte Buddyは、 Implementation
インターフェイスのインスタンスによって動的に定義されたメソッド実装を表現します。前の例では、このインターフェイスを実装するFixedValue
すでに実証されています。このインターフェイスを実装することにより、バイトバディのユーザーは、メソッドのカスタムバイトコードを定義する長さに移動できます。ただし、通常、Plain Javaでどのメソッドを実装できるようにするMethodDelegation
などのBYTE Buddyの事前定義された実装を使用する方が簡単です。この実装を使用することは、コントロールフローをPojoに委任することで動作するため、簡単です。そのようなPojoの例として、Byte Buddyは、たとえば、次のクラスの唯一の方法への呼び出しをリダイレクトできます。
public class GreetingInterceptor {
public Object greet ( Object argument ) {
return "Hello from " + argument ;
}
}
上記のGreetingInterceptor
、バイトバディタイプに依存しないことに注意してください。これは、Byte Buddyが生成するクラスのどれも、クラスパスでバイトバディを必要としないため、朗報です。上記のGreetingInterceptor
考えると、Byte Buddyを使用してJava 8 java.util.function.Function
インターフェイスとその抽象的なapply
方法を実装できます。
Class <? extends java . util . function . Function > dynamicType = new ByteBuddy ()
. subclass ( java . util . function . Function . class )
. method ( ElementMatchers . named ( "apply" ))
. intercept ( MethodDelegation . to ( new GreetingInterceptor ()))
. make ()
. load ( getClass (). getClassLoader ())
. getLoaded ();
assertThat (( String ) dynamicType . newInstance (). apply ( "Byte Buddy" ), is ( "Hello from Byte Buddy" ));
上記のコードを実行すると、Byte BuddyはJavaのFunction
インターフェイスを実装し、以前に定義したGreetingInterceptor
Pojoのインスタンスへの代表団としてのapply
方法を実装します。これで、 Function::apply
メソッドが呼び出されるたびに、コントロールフローがGreetingInterceptor::greet
に派遣され、後者のメソッドの戻り値はインターフェイスのメソッドから返されます。
インターセプターは、インターセプターのパラメーターに注釈を付けることにより、より一般的な入力と出力を使用するように定義できます。 Byte Buddyが注釈を発見すると、ライブラリはインターセプターパラメーターが必要とする依存関係を注入します。より一般的なインターセプターの例は、次のクラスです。
public class GeneralInterceptor {
@ RuntimeType
public Object intercept ( @ AllArguments Object [] allArguments ,
@ Origin Method method ) {
// intercept any method of any signature
}
}
上記のインターセプターを使用すると、傍受されたメソッドを一致させて処理できます。たとえば、 Function::apply
、メソッドの引数は配列の単一要素として渡されます。また、 Fuction::apply
への参照Method
、 @Origin
Annotationによるインターセプターの2番目の引数として渡されます。メソッドの@RuntimeType
アノテーションを宣言することにより、BYTE BUDDYは、これが必要な場合、傍受されたメソッドの返された値に最終的に返された値をキャストします。そうすることで、Byte Buddyは自動ボクシングとボクシングも適用します。
すでに述べた注釈に加えて、他にも事前定義された注釈がたくさんあります。たとえば、 Runnable
またはCallable
タイプで@SuperCall
アノテーションを使用する場合、BYTE BUDDYは、そのような方法が存在する場合に非抽象スーパーメソッドの呼び出しを可能にするプロキシインスタンスを注入します。また、Byte Buddyがユースケースをカバーしていなくても、Byte Buddyはカスタム注釈を定義するための拡張メカニズムを提供します。
これらの注釈を使用すると、コードをバディに結び付けることが期待されるかもしれません。ただし、Javaは、クラスローダーに表示されない場合に注釈を無視します。これにより、生成されたコードはバイトバディなしでまだ存在できます! MethodDelegation
およびその事前定義されたすべての注釈の詳細については、 JavadocとByte Buddyのチュートリアルで見つけることができます。
Byte Buddyは、サブクラスの作成に限定されませんが、既存のコードを再定義することもできます。そのために、Byte Buddyは、いわゆるJavaエージェントを定義するための便利なAPIを提供します。 Javaエージェントは、ランタイム中に既存のJavaアプリケーションのコードを変更するために使用できる、古いJavaプログラムです。例として、Byte Buddyを使用してメソッドを変更して実行時間を印刷できます。このため、最初に前の例でインターセプターと同様のインターセプターを定義します。
public class TimingInterceptor {
@ RuntimeType
public static Object intercept ( @ Origin Method method ,
@ SuperCall Callable <?> callable ) {
long start = System . currentTimeMillis ();
try {
return callable . call ();
} finally {
System . out . println ( method + " took " + ( System . currentTimeMillis () - start ));
}
}
}
Javaエージェントを使用して、このインターセプターをTypeDescription
のElementMatcher
と一致させるすべてのタイプに適用できるようになりました。この例では、 Timed
で終了する名前を持つすべてのタイプに上記のインターセプターを追加することを選択します。これは簡単にするために行われますが、注釈はおそらく生産エージェントにそのようなクラスをマークするためのより適切な代替手段になります。 BYTE BuddyのAgentBuilder
APIを使用して、Javaエージェントを作成することは、次のエージェントクラスを定義するのと同じくらい簡単です。
public class TimerAgent {
public static void premain ( String arguments ,
Instrumentation instrumentation ) {
new AgentBuilder . Default ()
. type ( ElementMatchers . nameEndsWith ( "Timed" ))
. transform (( builder , type , classLoader , module , protectionDomain ) ->
builder . method ( ElementMatchers . any ())
. intercept ( MethodDelegation . to ( TimingInterceptor . class ))
). installOn ( instrumentation );
}
}
Javaのmain
方法と同様に、 premain
メソッドは、再定義を適用するJavaエージェントへのエントリポイントです。 1つの議論として、Javaエージェントは、Runtimeクラスの再定義のためにByte BuddyがJVMの標準APIに接続できるようにするInstrumentation
インターフェイスのインスタンスを受信します。
このプログラムは、 TimerAgent
を指すPremain-Class
属性を備えたマニフェストファイルとともにパッケージ化されています。結果のJARファイルは、 -javaagent:timingagent.jar
をクラスパスに追加するのと同様に、Javaアプリケーションに任意のJavaアプリケーションに追加できます。エージェントがアクティブになると、 Timed
で終了するすべてのクラスがコンソールに実行時間を印刷するようになりました。
Byte Buddyは、クラスファイル形式の変更を無効にしてAdvice
インストルメンテーションを使用することにより、いわゆるランタイム添付ファイルを適用することもできます。詳細については、 Advice
のJavadocとAgentBuilder
クラスを参照してください。 Byte Buddyは、 ByteBuddy
インスタンスを介して、またはByte Buddy MavenおよびGradleプラグインを使用して、Javaクラスの明示的な変更を提供します。
Byte Buddyは包括的なライブラリであり、Byte Buddyの機能の表面を引っ掻いただけです。ただし、Byte Buddyは、クラスを作成するためのドメイン固有の言語を提供することにより、使いやすいことを目指しています。ほとんどのランタイムコード生成は、読み取り可能なコードを作成し、Javaのクラスファイル形式の知識なしに実行できます。 Byte Buddyの詳細については、Byte BuddyのWebページにこのようなチュートリアルを見つけることができます(中国語の翻訳もあります)。
さらに、Byte Buddyには、詳細なコード内のドキュメントと広範なテストケースのカバレッジが付属しています。これは、コードの例としても機能します。最後に、WikiのByte Buddyに関する記事とプレゼンテーションの最新リストを見つけることができます。 BYTE Buddyを使用する場合は、プロジェクトの依存関係の維持に関する以下の情報を必ずお読みください。
Byte Buddyの使用は無料であり、ライセンスの購入を必要としません。ただし、ライブラリを最大限に活用したり、簡単なスタートを切ったりするには、トレーニング、開発時間、またはサポートプランを購入することができます。レートは、エンゲージメントの範囲と期間に依存します。詳細については、[email protected]にご連絡ください。
Byte BuddyはTideliftにリストされています。明示的なサポートを購入したい範囲でBYTE Buddyを使用していない場合、一般的にオープンソースコミュニティをサポートしたい場合は、サブスクリプションを検討してください。
GitHubスポンサーを介して私の仕事をサポートできます。このオプションは、単純な支払いチャネルを探しており、見返りにサポートを期待していない商業俳優向けのみを目的としていることに注意してください。 GitHubスポンサーを介したサポートは、VATコンプライアンスを維持することはできません。代わりに直接サポート契約に連絡してください。
Stack OverflowまたはByte Buddyメーリングリストで、質問のアーカイブとしても役立つバイトバディメーリングリストで一般的な質問をすることができます。もちろん、バグレポートは商業計画の外でも考慮されます。オープンソースプロジェクトの場合、Byte Buddyを使用するための拡張ヘルプを受け取ることができる場合があります。
Byte Buddyは、ASMの上に書かれています。ASMは、コンパイルされたJavaクラスを読んで執筆するための成熟したよくテストされたライブラリです。高度なタイプの操作を可能にするために、Byte BuddyはASM APIを意図的にユーザーに公開しています。もちろん、ASMの直接的な使用は完全にオプションのままであり、ほとんどのユーザーはそれを必要としないでしょう。この選択は、BYTE Buddyのユーザーが高レベルの機能に拘束されないようにされたが、必要に応じて大騒ぎなくカスタム実装を実装できるようにされました。
ASMは以前にパブリックAPIを変更しましたが、ライブラリのバージョン4から始まるAPI互換性のメカニズムを追加しました。このような古いバージョンとのバージョンの競合を回避するために、BYTE BuddyはASM依存関係を独自の名前空間に再パッケージ化します。 ASMを直接使用する場合、 byte-buddy-dep
Artifactは、ASMに明示的な依存関係を持つByte Buddyのバージョンを提供します。そうする場合、バージョンの競合を避けるために、バイトバディとASMの両方を名前空間に再パッケージ化する必要があります。
このプロジェクトのセキュリティポリシーに注意してください。
Byte Buddyは、バージョン5のすべてのJVMバージョンでの実行をサポートし、1つの瓶で以降にサポートしています。これは、積極的に更新されていない古いまたは不明なアプリケーションをサポートする必要があるJavaエージェントの開発を容易にするために行われます。これを可能にしながら、モダンなJavaやCDSやStack Mapフレームを使用したクラスの検証などの機能をサポートするために、バージョン5と8のクラスファイルを含むマルチリリースジャーとしてのバイトバディ船のメインジャーです。その結果、バイトバディのジャーサイズは予想されるように高くなります。 JARファイルサイズは通常、問題ではありません。バイトバディのクラスの大部分はロードされないためです。しかし、Javaエージェントを配布する場合、ファイルサイズが問題になる可能性があります。エージェントはすでに単一の瓶としてバンドルされる必要があるため、この問題を軽減するために、基本的なJava 5バージョンまたはマルチリリースJava 8バージョンの含まれるクラスファイルのいずれかを削除することをお勧めします。これは、Mavenシェードプラグインなど、この目的のためにほとんどのビルドプラグインによってサポートされています。
Byte Buddyは、リベラルでビジネスに優しいApacheライセンスバージョン2.0の下でライセンスされており、Githubで自由に利用できます。さらに、 BYTE-BUDDY Distributionは、3節BSDライセンスの下でリリースされるASMをバンドルします。
Byte Buddy Binariesは、Maven CentralおよびJCenterのリポジトリに公開されています。アーティファクト署名は、Byte Buddy 1.10.3から始まるこのPGP公開キーに対して検証できます。古いバージョンは、この古くて弱い証明書に対して検証できます。
このプロジェクトは、Mavenを使用して構築されています。あなたのシェルから、プロジェクトのクローニングと構築は次のようになります:
git clone https://github.com/raphw/byte-buddy.git
cd byte-buddy
mvn package
これらのコマンドでは、Byte BuddyがGithubからクローン化され、マシン上に構築されます。さらにビルドオプションがルートPOMファイルにリストされています。 BYTE BUDDYは、少なくともバージョン6のJDKで構築できます。ただし、バージョン6と7のビルドには、暗号化されていないHTTPの使用が必要であるため、少なくともバージョン8のJDKを使用することをお勧めします。そのサポートは、このJDKバージョンに対するテストを実行するためのみであり、中間の攻撃にさらされる可能性があります。したがって、これらのビルドは避ける必要があります。 BYTE Buddyは現在、CIサーバー上のJDKのバージョン6以降についてテストされています。
バグを報告するためにGitHubの問題トラッカーを使用してください。コードをコミットするときは、機能の機能を証明したり、バグ修正を示したりするテストケースを提供してください。さらに、既存のテストケースを壊していないことを確認してください。可能であれば、時間をかけていくつかのドキュメントを書いてください。機能要求または一般的なフィードバックについては、問題トラッカーを使用したり、メーリングリストでお問い合わせください。
BYTE Buddyの作業も、プロジェクトに定期的なリソースと注意を払っているサポーターの列のおかげで可能です。それらのサポーターとその提供物を見てください。