未使用のBeanとそれを取り除く理由
Quarkusはビルド時重視のスタックです。つまり、アプリケーションの起動時間とメモリ使用量を改善するために、ビルド時に可能な限りのことをしようとします。しかし、既存のフレームワークやライブラリの多くがこの設計上の選択を考慮していないため、必ずしも簡単でわかりやすいものではありません。CDIはその一つです。
なぜわざわざ
従来のCDIコンテナは、アプリケーションのブートストラップ時にすべてのBeanを見つけようとします。各Beanに対して、メタデータオブジェクトが作成され、アプリケーションの寿命まで保持されます。これにより、ランタイムにある程度の動的性を持たせることができますが、メモリ消費やアプリケーションの起動時間に関しては最適ではありません。一方、Quarkusは、デフォルトでビルド時に 未使用の Bean、Interceptor、Decoratorをすべて検出して削除しようとします。その理由は簡単です。この最適化により、生成されるクラスの量と実行時に使用されるメタデータオブジェクトの数を最小限に抑え、メモリを節約することができます。
生成されたクラスについて。Beanの検出、検証、すべてのコンポーネントのワイヤリングは、すべてビルド時に実行されます。Quarkusでは、収集したメタデータをバイトコードに格納し、各Beanに対して1つ以上のクラスを生成します。いくつかの基本的なCDI API要件を満たすために、Beanは少なくとも対応する javax.enterprise.inject.spi.Bean
の実装を持っています。それが通常のスコープ付きBeanである場合,クライアントプロキシクラスも生成されなければなりません。最後に,インターセプトされたBean及び装飾されたBeanは,さらにいくつかの内部構造を必要とします。
アプリケーションに、実際にはどこにも使われていない50個のBeanが含まれているとします。それらが通常のスコープ(例: @ApplicationScoped
)を持ち、インターセプトされた場合(例: @Transactional
でアノテーションされたメソッドを宣言する)、150以上のクラスが生成されることが予想されます。そして、これらのクラスは全く役に立たない。それでも、コンテナはこれらの50以上のBeanメタデータクラスをインスタンス化し、参照を保持する必要があります。言うまでもなく、ネイティブイメージを生成する際に、Beanクラスや参照されるクラスをデッドコード排除の対象とすることはできません。そこで、Quarkusでは、これらのクラスをすべて削除するアルゴリズムを実装しています。
実際に削除されるものは?
Quarkusはまず、依存関係ツリーのルートを形成する、いわゆる 削除不可能な Beanを特定します。削除不可能なBean:
-
オブザーバー・メソッドを宣言するか
-
@Named
を通して指定された名前を持っているか -
@io.quarkus.arc.Unremovable
でアノテーションされているか -
quarkus.arc.unremovable-types
コンフィグ・プロパティによって除外されているか -
Quarkusのエクステンションで識別されているか。
最後の点が最も重要であると思われます。なぜならば、これはアプリケーションのエントリーポイントが削除できないようにする方法だからです。例えば、RESTEasyエクステンションで識別されるJAX-RSリソースクラスが良い例です。もう1つの例は、 @Scheduled
メソッドを宣言しているBeanで、Schedulerエクステンションによって識別されます。
_未使用の_Bean:
-
削除不可能ではないこと
-
依存関係ツリーのどの注入ポイントにも注入することが出来ないこと
-
依存関係ツリーのどの注入ポイントにも注入可能なプロデューサーを宣言していないこと
-
javax.enterprise.inject.Instance
またはjavax.inject.Provider
の注入ポイントに注入することが出来ないこと
最後に、未使用のインターセプターとデコレーターは、どのBeanとも関連付けられていません。
開発モードを使用している場合(例: ./mvnw quarkus:dev を実行している場合)、どのBeanが削除されているかの詳細な情報をDev UIで見ることができます。
|
主な欠点
しかし、1つ問題があります。Quarkusは、 CDI.current()
静的メソッドを介して実行されるプログラムによるルックアップを検出できません。そのため、あるビーンの削除で誤検出エラーが発生する可能性があります。つまり、実際に使用されているにもかかわらずビーンが削除されてしまうのです。このような場合には、ログに大きな警告が表示されます。もちろん、ユーザーやエクステンションの作者には、このようなエラーをなくす方法がいくつかあります。ユーザーにとって最も簡単な方法は、特別なアノテーション @io.quarkus.arc.Unremovable
を追加するか、 quarkus.arc.unremovable-types
設定プロパティを使用することです。最後に、 quarkus.arc.remove-unused-beans=false
の設定プロパティでこの最適化を無効にすることも可能です。