The English version of quarkus.io is the official project site. Translated sites are community supported on a best-effort basis.
このページを編集

コンテキストと依存性インジェクション

Quarkus DI ソリューション (ArC とも呼ばれる) は、 https://jakarta.ee/specifications/cdi/4.1/jakarta -cdi-spec-4.1.html[Jakarta コンテキストと依存性の注入 4.1、window="_blank"] 仕様に基づいています。 CDI Lite 仕様を実装し、その上に厳選された改良を加え、CDI Lite TCK に合格しました。 CDI Full は実装されていません。 the list of supported features and limitations も参照してください。 既存の CDI コードのほとんどは問題なく動作するはずですが、Quarkus のアーキテクチャーと目標に起因する小さな違いがいくつかあります。

CDI が初めての方は、最初に コンテキストと依存性注入(CDI)の紹介 を読むことを推奨します。
CDI インテグレーションガイド には、一般的な CDI 関連のインテグレーションのユースケースとソリューションのサンプルコードの詳細が記載されています。

1. Bean の検出

CDI での Bean 検出は、従来のデプロイメント構造と、基盤となるモジュールアーキテクチャーのアクセシビリティー要件が関係する複雑なプロセスです。 ただし、Quarkus は 簡略化された Bean 検出 を使用しています。 https://jakarta.ee/specifications/cdi/4.1/jakarta -cdi-spec-4.1.html#default_bean_discovery[bean 検出モード annotated、window="_blank"] および可視性境界のない単一の Bean アーカイブのみが存在します。

Bean のアーカイブは、次のものから合成されます。

  • アプリケーションクラス

  • beans.xml 記述子を含む依存関係 (内容は無視される)

  • Jandex インデックスを含む依存関係 (META-INF/jandex.idx)

  • application.propertiesquarkus.index-dependency で参照される依存関係

  • Quarkus の統合コード

https://jakarta.ee/specifications/cdi/4.1/jakarta -cdi-spec-4.1.html#bean_defining_annotations[bean 定義アノテーション、window="_blank"] のない Bean クラスは検出されません。 この動作は CDI によって定義されます。 ただし、宣言クラスに Bean 定義アノテーションが付けられていない場合でも、プロデューサーメソッドとフィールドおよびオブザーバーメソッドは検出されます (この動作は CDI で定義されているものと異なります)。 実際、宣言する Bean クラスは @Dependent でアノテーションが付けられていると見なされます。

Quarkus エクステンションは、追加のディスカバリールールを宣言することができます。たとえば、 @Scheduled ビジネスメソッドは、宣言するクラスが Bean 定義アノテーションでアノテーションされていなくても登録されます。

1.1. Jandex インデックスを生成する方法

Jandex インデックスを持つ依存関係は、Bean に対して自動的にスキャンされます。インデックスを生成するには、次のプラグインをビルドファイルに追加するだけです。

Maven
<build>
  <plugins>
    <plugin>
      <groupId>io.smallrye</groupId>
      <artifactId>jandex-maven-plugin</artifactId>
      <version>3.2.7</version>
      <executions>
        <execution>
          <id>make-index</id>
          <goals>
            <goal>jandex</goal>
          </goals>
        </execution>
      </executions>
    </plugin>
  </plugins>
</build>
Gradle (Groovy DSL)
plugins {
    id 'org.kordamp.gradle.jandex' version '1.0.0'
}

最新のプラグインバージョンは、 Gradle プラグインポータル で確認できます。

Gradle (Kotlin DSL)
plugins {
    id("org.kordamp.gradle.jandex") version "1.0.0"
}

最新のプラグインバージョンは、 Gradle プラグインポータル で確認できます。

依存関係を変更できなくても、 quarkus.index-dependency エントリーを application.properties に追加することでインデックスを作成できます。

quarkus.index-dependency.<name>.group-id=
quarkus.index-dependency.<name>.artifact-id=(this one is optional)
quarkus.index-dependency.<name>.classifier=(this one is optional)
artifact-id が指定されていない場合は、 group-id が指定された依存関係はすべてインデックス化されます。

たとえば、次のエントリーは、 org.acme:acme-api 依存関係が確実にインデックス化されるようにします。

application.properties の例
quarkus.index-dependency.acme.group-id=org.acme (1)
quarkus.index-dependency.acme.artifact-id=acme-api (2)
1 acme は、名前で識別される依存関係のグループ ID です。
2 値は、名前 acme で識別される依存関係のアーティファクト ID です。

1.2. ディスカバリーから型と依存関係を除外する方法

サードパーティーのライブラリーからのいくつかの Bean が Quarkus で正しく動作しないことがあります。典型的な例は、ポータブルエクステンションを注入する Bean です。このような場合は、型や依存関係を Bean の検出から除外することができます。 quarkus.arc.exclude-types プロパティーは、除外すべきクラスに一致するために使用される文字列値のリストを受け入れます。

Table 1. 値の例

説明

org.acme.Foo

クラスの完全修飾名と一致させる

org.acme.*

org.acme パッケージとクラスを一致させる

org.acme.**

パッケージが org.acme で始まるクラスを一致させる

Bar

クラスのシンプルな名前に一致する

application.properties の例
quarkus.arc.exclude-types=org.acme.Foo,org.acme.*,Bar (1)(2)(3)
1 タイプ org.acme.Foo を除外します。
2 org.acme パッケージからすべてのタイプを除外します。
3 シンプルな名前が Bar であるすべてのタイプを除外します。

また、除外しなければ Bean をスキャンする、依存関係のあるアーティファクトを除外することも可能です。たとえば、 beans.xml 記述子を含んでいる場合です。

application.properties の例
quarkus.arc.exclude-dependency.acme.group-id=org.acme (1)
quarkus.arc.exclude-dependency.acme.artifact-id=acme-services (2)
1 acme は、名前で識別される依存関係のグループ ID です。
2 値は、名前 acme で識別される依存関係のアーティファクト ID です。

2. ネイティブ実行可能ファイルとプライベートメンバー

Quarkus は GraalVM を使用してネイティブ実行可能ファイルを構築します。 GraalVM の制限の 1 つは、リフレクション の使用量です。 リフレクション操作はサポートされていますが、関連するすべてのメンバーを明示的にリフレクションに登録する必要があります。 これらの登録により、ネイティブ実行可能ファイルが大きくなります。

また、Quarkus DI がプライベートメンバーにアクセスする必要がある場合は、リフレクションを使用しなければなりません。そのため、Quarkus ユーザーは、プライベートメンバーを Bean で 使用しないこと が推奨されています。これには、インジェクションフィールド、コンストラクターとイニシャライザー、observer メソッド、producer メソッドおよび producer フィールド、disposers メソッドおよび interceptor メソッドが含まれます。

プライベートメンバーの使用を回避するには、package-private 修飾子を使うことができます。

@ApplicationScoped
public class CounterBean {

    @Inject
    CounterService counterService; (1)

    void onMessage(@Observes Event msg) { (2)
    }
}
1 package-private injection フィールド。
2 package-private observer メソッド。

あるいはコンストラクター注入も可能です:

@ApplicationScoped
public class CounterBean {

    private CounterService service;

    CounterBean(CounterService service) { (1)
      this.service = service;
    }
}
1 パッケージプライベートコンストラクター注入。この特定の場合、 @Inject は任意です。

3. サポートされている機能と制限

CDI Lite 仕様は完全にサポートされています。 CDI Full の次の機能もサポートされています。

  • デコレーター

    • Event などのビルトイン Bean の装飾はサポートされていません

  • BeanManager

    • BeanContainer メソッドに加えて、 getInjectableReference()resolveDecorators() のメソッドがサポートされています。

  • @SessionScoped

    • Undertow エクステンションのみ。詳細は こちら を参照してください。

メソッド呼び出し 実装は非同期メソッドをサポートします。 次のメソッドは非同期とみなされ、 @Dependent インスタンスは非同期アクションが完了したときにのみ破棄されます。

  • 戻り値の型として CompletionStageUni、または Multi を宣言するメソッド

これらの追加機能は、CDI Lite TCK では対応していません。

4. 標準外の機能

4.1. Bean インスタンス化の意向

4.1.1. デフォルトでは遅延

デフォルトでは、CDI Bean は必要なときに作成されます。何を正確に「必要とされる」かは、Bean のスコープに依存します。

  • 注入されたインスタンス (仕様に応じたコンテキスト参照) からメソッドが呼び出される場合は、通常のスコープ付き Bean (@ApplicationScoped@RequestScoped など) が必要になります。

    言い換えれば、通常のスコープ付き Bean を注入しても、Bean のコンテキストに基づいたインスタンスの代わりに クライアントプロキシー が注入されるため、十分ではありません。

  • 注入時に 疑似スコープを持つ Bean (@Dependent および @Singleton) が作成されます。

遅延インスタンス化の例
@Singleton // => pseudo-scope
class AmazingService {
  String ping() {
    return "amazing";
  }
}

@ApplicationScoped // => normal scope
class CoolService {
  String ping() {
    return "cool";
  }
}

@Path("/ping")
public class PingResource {

  @Inject
  AmazingService s1; (1)

  @Inject
  CoolService s2; (2)

  @GET
  public String ping() {
    return s1.ping() + s2.ping(); (3)
  }
}
1 注入は、 AmazingService のインスタンス化をトリガーします。
2 注入自体は、 CoolService のインスタンス化にはなりません。クライアントプロキシーが注入されます。
3 注入されたプロキシーに対する最初の呼び出しは、 CoolService のインスタンス化をトリガーします。

4.1.2. スタートアップイベント

ただし、Bean の即時(eagerly)インスタンス化が必要な場合は、次のことができます。

  • StartupEvent のオブザーバーを宣言します。この場合、Bean のスコープは重要ではありません。

    @ApplicationScoped
    class CoolService {
      void startup(@Observes StartupEvent event) { (1)
      }
    }
    1 CoolService は、起動時に作成され、オブザーバーメソッドの呼び出しを処理します。
  • StartupEvent のオブザーバーで Bean を使用します - デフォルトでは遅延 で説明されているように、通常のスコープ Bean を使用する必要があります。

    @Dependent
    class MyBeanStarter {
    
      void startup(@Observes StartupEvent event, AmazingService amazing, CoolService cool) { (1)
        cool.toString(); (2)
      }
    }
    1 AmazingService は注入時に作成されます。
    2 CoolService は通常のスコープ付き Bean であるため、強制的にインスタンス化するために挿入されたプロキシーにメソッドを呼び出さなければなりません。
  • Startup annotation: で説明したように、 @io.quarkus.runtime.Startup で Bean をアノテーションします。

    @Startup (1)
    @ApplicationScoped
    public class EagerAppBean {
    
       private final String name;
    
       EagerAppBean(NameGenerator generator) { (2)
         this.name = generator.createName();
       }
    }
    1 @Startup でアノテーションされた各Beanに対して、 StartupEvent の合成オブザーバが生成されます。デフォルトの優先度が使用されます。
    2 Beanのコンストラクタは、アプリケーションの起動時に呼び出され、結果として得られるコンテキストインスタンスがアプリケーションのコンテキストに格納されます。
Quarkus ユーザーは、 アプリケーションの初期化と終了 のガイドで説明されているように、常に @Initialized(ApplicationScoped.class) よりも @Observes StartupEvent を選択することが推奨されます。

4.2. リクエストコンテキストのライフサイクル

リクエストコンテキストは次の場合もアクティブになっています:

  • 同期オブザーバメソッドの通知中

リクエストコンテキストは破棄されます:

  • 通知が開始したときにまだアクティブではなかった場合はイベントのオブザーバー通知が完了した後

オブザーバー通知のためにリクエストコンテキストが初期化されると、修飾子 @Initialized(RequestScoped.class) を持つイベントが発生します。さらに、修飾子 @BeforeDestroyed(RequestScoped.class) および @Destroyed(RequestScoped.class) を持つイベントは、リクエストコンテキストが破棄されたときに発生します。

4.2.1. リクエストコンテキストのアクティブ化のトレースログを有効にする方法

ロガー io.quarkus.arc.requestContextTRACE レベルを設定し、後でログ出力の分析を試みることができます。

application.properties の例
quarkus.log.category."io.quarkus.arc.requestContext".min-level=TRACE (1)
quarkus.log.category."io.quarkus.arc.requestContext".level=TRACE
1 また、関連するカテゴリーの最小ログレベルを調整する必要があります。

4.3. 修飾された注入フィールド

CDI では、フィールド注入ポイントを宣言する場合は @Inject と任意で修飾子のセットを使用する必要があります。

  @Inject
  @ConfigProperty(name = "cool")
  String coolProperty;

Quarkus では、注入されたフィールドが少なくとも 1 つの修飾子を宣言している場合は、 @Inject アノテーションを完全にスキップすることができます。

  @ConfigProperty(name = "cool")
  String coolProperty;
後述する特別なケースを除いて、コンストラクターとメソッドの注入には @Inject が必要です。

4.4. 簡略化されたコンストラクター注入

CDI では、通常のスコープ付き Bean は常に no-args コンストラクターを宣言しなければなりません (このコンストラクターは、他のコンストラクターを宣言しない限り、通常はコンパイラーによって生成されます)。しかし、この要件はコンストラクターの注入を複雑にします。CDI で動作させるためにはダミーの no-args コンストラクターを提供する必要があります。

@ApplicationScoped
public class MyCoolService {

  private SimpleProcessor processor;

  MyCoolService() { // dummy constructor needed
  }

  @Inject // constructor injection
  MyCoolService(SimpleProcessor processor) {
    this.processor = processor;
  }
}

Quarkus では、通常のスコープ付き Bean のためにダミーのコンストラクターを宣言する必要はありません。自動的に生成されます。また、コンストラクターが 1 つしかない場合は、 @Inject の必要性はありません。

@ApplicationScoped
public class MyCoolService {

  private SimpleProcessor processor;

  MyCoolService(SimpleProcessor processor) {
    this.processor = processor;
  }
}
変数無しコンストラクターを宣言していないクラスを Bean クラスが継承している場合は、変数無しコンストラクターは自動的に生成されません。

4.5. 未使用の Bean の削除

デフォルトでは、コンテナーはビルド中に未使用の Bean、インターセプター、デコレータをすべて削除しようとします。 この最適化により、生成されるクラスの量が最小限に抑えられ、メモリーが節約されます。 ただし、Quarkus は CDI.current() 静的メソッドを介して実行されるプログラムによる検索を検出できません。 したがって、削除によって誤検知エラーが発生する可能性があります。つまり、Bean は実際には使用されているにもかかわらず削除されることになります。 このような場合、ログに大きな警告が表示されます。 ユーザーとエクステンションの作者には how to eliminate false positives のオプションが複数あります。

この最適化は、 quarkus.arc.remove-unused-beansnone または false に設定することで無効にすることができます。Quarkus では、アプリケーションの Bean は未使用かどうかにかかわらず削除されず、アプリケーション以外のクラスは通常通りに最適化が行われるという中間モードも提供されています。このモードを使用するには、 quarkus.arc.remove-unused-beansfwk または framework に設定します。

4.5.1. 何が削除されましたか?

Quarkusはまず、依存関係ツリーのルートを形成する、いわゆる 削除不可能な Beanを特定します。良い例は、Jakarta RESTリソースクラスや、 @Scheduled メソッドを宣言している Bean です。

削除できない Bean:

  • excluded from removal、または

  • @Named を通して指定された名前を持っているか、

  • オブザーバーメソッドを宣言している場合。

未使用 の Bean:

  • 削除不可 ではない、および

  • 削除不可能 な Bean の依存ツリー内のどの注入ポイントにも注入できない。

  • 依存性ツリー内のどの注入ポイントにも注入可能なプロデューサーを宣言していない。

  • jakarta.enterprise.inject.Instance または jakarta.inject.Provider 注入ポイントへの注入には適格ではない。

  • @Inject @All List<> 注入ポイントに注入する資格がない。

未使用のインターセプターとデコレーターは、どの Bean とも関連付けられていない。

開発モード (./mvnw quarkus:dev を実行) を使用すると、削除される Bean に関する詳細情報を確認できます。

  1. コンソールで - application.properties で DEBUG レベルを有効にするだけです。例: quarkus.log.category."io.quarkus.arc.processor".level=DEBUG

  2. 関連する開発 UI ページ

4.5.2. 誤検知を排除する方法

ユーザーは、コンテナーに @io.quarkus.arc.Unremovable をアノテーションすることで、(上で指定したルールをすべて満たしていても) 特定の Bean を削除しないように指示することができます。このアノテーションは、クラス、producer メソッド、producer フィールドに置くことができます。

これは常に可能ではないので、 application.properties を通して同じことを実現するオプションがあります。 quarkus.arc.unremovable-types プロパティーは、Bean の名前やパッケージに基づいて一致させるための文字列値のリストを受け付けます。

Table 2. 値の例

説明

org.acme.Foo

bean クラスの完全修飾名と一致させる

org.acme.*

Bean クラスのパッケージが org.acme である Bean に一致します。

org.acme.**

Bean クラスのパッケージが org.acme で開始する Bean に一致します。

Bar

Bean クラスのシンプルな名前に一致します。

application.properties の例
quarkus.arc.unremovable-types=org.acme.Foo,org.acme.*,Bar

さらに、エクステンションは UnremovableBeanBuildItem を生成することで、可能性のある誤検出を排除することができます。

4.6. デフォルトの Bean

Quarkus は、CDI が現在サポートしていない機能を追加します。これは、利用可能な手段 (Beanクラス、producer、合成 Bean など) で同等の型と修飾子を持つ他の Bean が宣言されていない場合に、条件付きで Bean を宣言することです。これは、 @io .quarkus.Arc.DefaultBean アノテーションを使用して行われ、例を挙げて説明するのが最善です。

以下のコードのようにいくつかの CDI Bean を宣言する Quarkus エクステンションがあるとします。

@Dependent
public class TracerConfiguration {

    @Produces
    public Tracer tracer(Reporter reporter, Configuration configuration) {
        return new Tracer(reporter, configuration);
    }

    @Produces
    @DefaultBean
    public Configuration configuration() {
        // create a Configuration
    }

    @Produces
    @DefaultBean
    public Reporter reporter(){
        // create a Reporter
    }
}

アイデアは、エクステンションがユーザーのために自動設定を行い、多くのボイラープレートを排除するということです。必要な場所であれば、 @InjectTracer にすることができます。私たちのアプリケーションで、設定された Tracer を利用しようとする場合は、、カスタムの Reporter を提供するなど、少しカスタマイズする必要があります。アプリケーションで必要になるのは、次のようなものだけです。

@Dependent
public class CustomTracerConfiguration {

    @Produces
    public Reporter reporter(){
        // create a custom Reporter
    }
}

@DefaultBean では、エクステンション (またはそのための他のコード) が Quarkus がサポートする何らかの方法でその型の Bean が提供されている場合、バックオフ中にデフォルトを提供することができます。

デフォルトの Bean はオプションで @jakarta.annotation.Priority を宣言できます。 優先度が定義されていない場合は、 @Priority (0) が想定されます。 優先度の値は、Bean の順序付けと、複数の一致するデフォルト Bean の曖昧さを解消するための型安全な解決に使用されます。

@Dependent
public class CustomizedDefaultConfiguration {

    @Produces
    @DefaultBean
    @Priority(100)
    public Configuration customizedConfiguration(){
        // create a customized default Configuration
        // this will have priority over previously defined default bean
    }
}

4.7. Quarkus ビルドプロファイルの Bean の有効化

Quarkus は、CDI が現在サポートしていない機能を追加しました。これは、Quarkus のビルドタイムプロファイルが有効になっているときに条件付きで Bean を有効にするというもので、 @io.quarkus.arc.profile.IfBuildProfile@io.quarkus.arc.profile.UnlessBuildProfile のアノテーションを使用します。 @io.quarkus.arc.DefaultBean と合わせて使用すると、これらのアノテーションにより、異なるビルドプロファイルに対して異なる Bean 構成を作成することができます。

たとえば、アプリケーションが Tracer という名前の Bean を含んでいるとします。この Bean は、テストや開発モードでは何もする必要はありませんが、本番の成果物に対しては通常の能力で動作します。このような Bean を作成する洗練された方法は以下の通りです。

@Dependent
public class TracerConfiguration {

    @Produces
    @IfBuildProfile("prod")
    public Tracer realTracer(Reporter reporter, Configuration configuration) {
        return new RealTracer(reporter, configuration);
    }

    @Produces
    @DefaultBean
    public Tracer noopTracer() {
        return new NoopTracer();
    }
}

代わりに、 Tracer Bean も開発モードで動作し、デフォルトではテストのために何もしないことが要求される場合は、 @UnlessBuildProfile が理想的です。コードは次のようになります。

@Dependent
public class TracerConfiguration {

    @Produces
    @UnlessBuildProfile("test") // this will be enabled for both prod and dev build time profiles
    public Tracer realTracer(Reporter reporter, Configuration configuration) {
        return new RealTracer(reporter, configuration);
    }

    @Produces
    @DefaultBean
    public Tracer noopTracer() {
        return new NoopTracer();
    }
}
ランタイムプロファイルは、 @IfBuildProfile および @UnlessBuildProfile を使用した Bean 解決には影響を及ぼしません。
ステレオタイプで @IfBuildProfile@UnlessBuildProfile を使用することもできます。

4.8. Quarkus ビルドプロパティーの Bean を有効にする

Quarkus は、CDI が現在サポートしていない機能を追加します。これは、Quarkus のビルド時のプロパティーに特定の値がある場合とない場合に、 @io.quarkus.arc.properties.IfBuildProperty および @io.quarkus.arc.properties.UnlessBuildProperty アノテーションを介して 条件付きで Bean を有効にする機能です。 これらのアノテーションを @io.quarkus.arc.DefaultBean と組み合わせて使用すると、異なるビルドプロパティーに対して異なる Bean 設定を作成できます。

Tracer を使用して上で述べたシナリオも、以下のように実装することができます。

@Dependent
public class TracerConfiguration {

    @Produces
    @IfBuildProperty(name = "some.tracer.enabled", stringValue = "true")
    public Tracer realTracer(Reporter reporter, Configuration configuration) {
        return new RealTracer(reporter, configuration);
    }

    @Produces
    @DefaultBean
    public Tracer noopTracer() {
        return new NoopTracer();
    }
}
@IfBuildProperty@UnlessBuildProperty は繰り返し可能なアノテーションです。つまり、これらのアノテーションで定義された条件の すべて が満たされた場合にのみBeanが有効になります。

代わりに、 some.tracer.enabled プロパティーが false でない場合にのみ RealTracer Bean が使用されることが要求される場合は、 @UnlessBuildProperty が理想的です。コードは以下のようになります。

@Dependent
public class TracerConfiguration {

    @Produces
    @UnlessBuildProperty(name = "some.tracer.enabled", stringValue = "false")
    public Tracer realTracer(Reporter reporter, Configuration configuration) {
        return new RealTracer(reporter, configuration);
    }

    @Produces
    @DefaultBean
    public Tracer noopTracer() {
        return new NoopTracer();
    }
}
実行時に設定されたプロパティーは、 @IfBuildProperty を使用しても Bean の解決に全く影響しません。
ステレオタイプで @IfBuildProperty@UnlessBuildProperty を使用することもできます。

4.9. 選択された代替の宣言

CDI では、代替の Bean は、 @Priority を使用してアプリケーションに対してグローバルに選択することも、 beans.xml 記述子を使用した Bean アーカイブ用に選択することもできます。Quarkus には単純化された Bean 検出があり、 beans.xml の内容は無視されます。

ただし、統合設定を使用して、アプリケーションの代替を選択することもできます。 quarkus.arc.selected-alternatives プロパティーは、代替 Bean を照合するために使用される文字列値のリストを受け入れます。 一致する値がある場合は、関連する Bean に Integer#MAX_VALUE の優先順位が使用されます。 @Priority で宣言された優先度またはステレオタイプで継承された優先度は上書きされます。

Table 3. 値の例

説明

org.acme.Foo

Bean クラス、または producer を宣言する Bean の Bean クラスの完全修飾名に一致します。

org.acme.*

Bean クラスのパッケージが org.acme である Bean に一致します。

org.acme.**

Bean クラスのパッケージが org.acme で開始する Bean に一致します。

Bar

Bean クラス、または producer を宣言する Bean の Bean クラスの単純名に一致します。

application.properties の例
quarkus.arc.selected-alternatives=org.acme.Foo,org.acme.*,Bar

4.10. 簡略化された Producer メソッドの宣言

CDI では、producer メソッドは常に @Produces とアノテーションされていなければなりません。

class Producers {

  @Inject
  @ConfigProperty(name = "cool")
  String coolProperty;

  @Produces
  @ApplicationScoped
  MyService produceService() {
    return new MyService(coolProperty);
  }
}

Quarkus では、producer メソッドにスコープアノテーション、ステレオタイプ、または修飾子が付いている場合は、 @Produces アノテーションを完全に省略できます。

class Producers {

  @ConfigProperty(name = "cool")
  String coolProperty;

  @ApplicationScoped
  MyService produceService() {
    return new MyService(coolProperty);
  }
}

4.11. 静的メソッドのインターセプション

インターセプターの仕様は、around-invoke メソッドを静的宣言してはならないことは明らかです。しかし、この制限は、主に技術的な制限によって設定されました。Quarkus は追加のクラス変換を可能にするビルド時指向のスタックであるため、この制限は適用されなくなりました。インターセプタ―バインディングで非プライベートの静的メソッドにアノテーションを付けることができます。

class Services {

  @Logged (1)
  static BigDecimal computePrice(long amount) { (2)
    BigDecimal price;
    // Perform computations...
    return price;
  }
}
1 Logged はインターセプターバインディングです。
2 各メソッドの呼び出しは、 Logged に関連付けられたインターセプターがある場合に傍受されます。

4.11.1. 制限

  • 下位互換性の理由から メソッドレベルのバインディング のみが考慮されます (そうでないとクラスレベルのバインディングを宣言している Bean クラスのStaticメソッドが突然傍受されてしまいます)。

  • プライベートなスタティックメソッドは決して傍受されません。

  • InvocationContext#getTarget() は明白な理由により null を返します。したがって、Staticメソッドを傍受するときに既存のインターセプターがすべて正しく動作するとは限りません。

    インターセプターは InvocationContext.getMethod() を使用してStaticメソッドを検出し、それに応じて動作を調整することができます。

4.12. final クラスとメソッドを処理する能力

通常の CDI では、 final としてマークされているクラス、または final メソッドを持つクラスは、プロキシー作成の対象になりません。これは、インターセプターと通常のスコープ Bean が正しく動作しないことを意味します。このような状況は、クラスおよびメソッドがデフォルトで `final`である Kotlin のような代替 JVM 言語で CDI を使用しようとするときに非常に一般的です。

しかし、Quarkus では、 quarkus.arc.transform-unproxyable-classestrue (デフォルト値) に設定すると、これらの制限を抑制することができます。

4.13. コンテナー管理型の並行処理

CDI Bean の標準的な同時実行制御メカニズムはありません。それにもかかわらず、Bean インスタンスは、複数のスレッドから同時に共有およびアクセスすることができます。その場合は、スレッドセーフでなければなりません。標準の Java コンストラクト (volatilesynchronizedReadWriteLock など) を使用するか、コンテナーに同時アクセスを制御させることができます。Quarkus は、 @io.quarkus.arc.Lock と、このインターセプタ―バインディング用の組み込みインターセプターを提供します。傍受された Bean のコンテキストインスタンスに関連付けられた各インターセプターインスタンスは、公平でない順序付けポリシーを持つ ReadWriteLock を保持しています。

io.quarkus.arc.Lock は通常のインターセプターバインディングであるため、任意のスコープを持つ任意の Bean に使用することができます。しかし、特に「共有」スコープ、たとえば @Singleton@ApplicationScoped に有益です。
コンテナー管理された並行処理の例
import io.quarkus.arc.Lock;

@Lock (1)
@ApplicationScoped
class SharedService {

  void addAmount(BigDecimal amount) {
    // ...changes some internal state of the bean
  }

  @Lock(value = Lock.Type.READ, time = 1, unit = TimeUnit.SECONDS) (2) (3)
  BigDecimal getAmount() {
    // ...it is safe to read the value concurrently
  }
}
1 クラスで宣言された (@Lock (Lock.type.Write) にマッピングされる) @Lock は、任意のビジネスメソッドの呼び出しに対して Bean インスタンスをロックするようにコンテナーに指示します。つまり、クライアントには「排他アクセス」があり、同時呼び出しは許可されません。
2 @Lock(Lock.Type.READ) は、クラスレベルで指定された値を上書きします。これは、Bean のインスタンスが @Lock(Lock.Type.WRITE) によってロックされていない限り、任意の数のクライアントが同時にメソッドを呼び出すことができることを意味します。
3 また、「待ち時間」を指定することもできます。指定した時間内にロックを取得できない場合は LockException が発生します。

4.14. 反復可能なインターセプターバインディング

Quarkusでは、 @Repeatable インターセプター結合アノテーションのサポートが制限されています。

インターセプタ―をコンポーネントにバインドする場合は、メソッドに対して複数の @Repeatable アノテーションを宣言できます。インターセプター仕様との相互作用に関する未解決の質問があるため、クラスとステレオタイプで宣言された反復可能なインターセプターバインディングはサポートされていません。これは将来追加される可能性があります。

たとえば、キャッシュをクリアするインターセプタ―があるとします。対応するインターセプタ―バインディングは @CacheInvalidateAll と呼ばれ、 @Repeatable として宣言されます。同時に 2 つのキャッシュをクリアしたい場合は、 @CacheInvalidateAll を 2 回追加します。

@ApplicationScoped
class CachingService {
  @CacheInvalidateAll(cacheName = "foo")
  @CacheInvalidateAll(cacheName = "bar")
  void heavyComputation() {
    // ...
    // some computation that updates a lot of data
    // and requires 2 caches to be invalidated
    // ...
  }
}

ここまで、インターセプタ―がどのように使用されるかを説明しました。では、インターセプターを作成するにはどうすれば良いでしょうか。

インターセプタ―のインターセプタ―バインディングを宣言する場合は、通常どおり、インターセプタ―クラスに複数の @Repeatable アノテーションを追加できます。 @Cached アノテーションの場合と同様に、アノテーションメンバーが @Nonbinding の場合は役に立ちませんが、それ以外の場合は重要になります。

たとえば、メソッド呼び出しを特定のターゲットに自動的に記録できるインターセプタ―があるとします。インターセプタ―バインディングアノテーション @Logged には、ログを保存する場所を指定する target というメンバーがあります。この実装は、コンソールログとファイルロギングに制限することができます。

@Interceptor
@Logged(target = "console")
@Logged(target = "file")
class NaiveLoggingInterceptor {
  // ...
}

他にも、異なるターゲットへのメソッド呼び出しをログに記録するためのインターセプターを提供することができます。

4.15. プログラムによるルックアップの結果をキャッシュする

特定の状況では、注入された jakarta.enterprise.inject.InstanceInstance.get() を介してプログラムで Bean インスタンスを取得することが実用的です。 ただし、仕様によれば、 get() メソッドは一致する Bean を識別し、コンテキスト参照を取得する必要があります。 その結果、 get() の各呼び出しから @Dependent Bean の新しいインスタンスが返されます。 さらに、このインスタンスは、注入された Instance の依存オブジェクトです。 この動作は明確に定義されていますが、予期しないエラーやメモリーリークが発生する可能性があります。 そのため、Quarkus には io.quarkus.arc.WithCaching アノテーションが付属しています。 このアノテーションが付けられた挿入された Instance は、 Instance#get() 操作の結果をキャッシュします。 結果は最初の呼び出しで計算され、 @Dependent Bean の場合でも、後続のすべての呼び出しに対して同じ値が返されます。

class Producer {

  AtomicLong nextLong = new AtomicLong();
  AtomicInteger nextInt = new AtomicInteger();

   @Dependent
   @Produces
   Integer produceInt() {
     return nextInt.incrementAndGet();
   }

   @Dependent
   @Produces
   Long produceLong() {
     return nextLong.incrementAndGet();
   }
}

class Consumer {

  @Inject
  Instance<Long> longInstance;

  @Inject
  @WithCaching
  Instance<Integer> intInstance;

  // this method should always return true
  // Producer#produceInt() is only called once
  boolean pingInt() {
    return intInstance.get().equals(intInstance.get());
  }

  // this method should always return false
  // Producer#produceLong() is called twice per each pingLong() invocation
  boolean pingLong() {
    return longInstance.get().equals(longInstance.get());
  }
}
io.quarkus.arc.InjectableInstance.clearCache() を使って、キャッシュされた値をクリアすることも可能です。この場合、 jakarta.enterprise.inject.Instance の代わりに Quarkus 固有の io.quarkus.arc.InjectableInstance を注入する必要があります。

4.16. プログラムによるルックアップで取得できる Bean を宣言的に選択する

jakarta.enterprise.inject.Instance を介したプログラムによる検索で取得できる Bean の集合を絞り込むことが有用な場合があります。典型的には,ユーザは,実行時設定プロパティに基づいて,インタフェースの適切な実装を選択する必要があります。

org.acme.Service インターフェイスを実装する 2 つの Bean があるとします。 実装で CDI 修飾子を宣言しない限り、 org.acme.Service を直接挿入できません。 ただし、 インスタンス<Service> の代わりに、すべての実装を反復処理して、正しい実装を手動で選択します。 または、 @LookupIfProperty アノテーションと @LookupUnlessProperty アノテーションを使用することもできます。 @LookupIfProperty は、実行時設定プロパティーが指定された値と一致する場合にのみ Bean を取得することを示します。 一方、 @LookupUnlessProperty は、実行時設定プロパティーが指定された値と一致しない場合にのみ Bean を取得することを示します。

@LookupIfProperty Example
 interface Service {
    String name();
 }

 @LookupIfProperty(name = "service.foo.enabled", stringValue = "true")
 @ApplicationScoped
 class ServiceFoo implements Service {

    public String name() {
       return "foo";
    }
 }

 @ApplicationScoped
 class ServiceBar implements Service {

    public String name() {
       return "bar";
    }
 }

 @ApplicationScoped
 class Client {

    @Inject
    Instance<Service> service;

    void printServiceName() {
       // This will print "bar" if the property "service.foo.enabled" is NOT set to "true"
       // If "service.foo.enabled" is set to "true" then service.get() would result in an AmbiguousResolutionException
       System.out.println(service.get().name());
    }
 }

4.17. プログラムによる検索で取得した Bean のソート

必要なタイプと修飾子に一致し、注入の対象となる Bean が複数ある場合は、使用可能な Bean インスタンスを反復処理 (またはストリーム処理) できます。 ストリームメソッドとイテレーターメソッドの両方で返される Bean は、 io.quarkus.arc.InjectableBean#getPriority() で定義された優先順位でソートされます。優先度の高いものが最初に来ます。 優先度が明示的に宣言されていない場合は、0 が想定されます。

interface Service {

}

@Priority(100)
@ApplicationScoped
class FirstService implements Service {

}

@Priority(10)
@ApplicationScoped
class SecondService implements Service {

}

@ApplicationScoped
class ThirdService implements Service {

}

@ApplicationScoped
class Client {

   @Inject
   Instance<Service> serviceInstance;

   void printServiceName() {
       if(service.isAmbiguous()){
           for (Service service : serviceInstance) {
                // FirstService, SecondService, ThirdService
           }
       }
   }
}

4.18. 複数の Bean インスタンスを直感的に注入する

CDIでは、 java.lang.Iterable を実装した jakarta.enterprise.inject.Instance を介して、複数のBeanインスタンス(別名:コンテキスト参照)を注入することが可能です。しかし、これは直感的に理解できるものではありません。そこで、Quarkusでは新しい方法を導入しました。 io.quarkus.arc.All 修飾子でアノテーションされた java.util.List を注入することができます。リスト内の要素の型は、検索を実行する際に必要な型として使用されます。

@ApplicationScoped
public class Processor {

     @Inject
     @All
     List<Service> services; (1) (2)
}
1 注入されたインスタンスは、disambiguated bean のコンテキスト参照の immutable list です。
2 このインジェクションポイントに必要なタイプは Service であり、追加の修飾子は宣言されていません。
リストは、 io.quarkus.arc.InjectableBean#getPriority() で定義された優先度でソートされます。優先度の高いものが最初に来ます。一般に、 @jakarta.annotation.Priority アノテーションを使用して、クラス Bean、プロデューサーメソッド、またはプロデューサーフィールドに優先度を割り当てることができます。

インジェクションポイントが @All 以外の修飾子を宣言していない場合、 @Any が使用されます。つまり、動作は @Inject @Any Instance<Service> と同等です。

io.quarkus.arc.InstanceHandle でラップされた Bean インスタンスのリストを注入することもできます。これは、関連する Bean メタデータを検査する必要がある場合に役立ちます。

@ApplicationScoped
public class Processor {

     @Inject
     @All
     List<InstanceHandle<Service>> services;

     public void doSomething() {
       for (InstanceHandle<Service> handle : services) {
         if (handle.getBean().getScope().equals(Dependent.class)) {
           handle.get().process();
           break;
         }
       }
     }
}
型変数もワイルドカードも @All List<> インジェクションポイントのタイプパラメーターとしては有効ではありません。つまり、 @Inject @All List<?> all はサポートされておらず、デプロイメントエラーになります。
Arc.container().listAll() メソッドを使用して、プログラムですべての Bean インスタンスハンドルのリストを取得することもできます。

4.19. メソッドとコンストラクターのクラスレベルのインターセプターバインディングを無視する

マネージド Bean がクラスレベルでインターセプターバインディングアノテーションを宣言する場合、対応する @AroundInvoke インターセプターがすべてのビジネスメソッドに適用されます。同様に、対応する @AroundConstruct インターセプターが Bean コンストラクターに適用されます。

たとえば、 @Logged バインディングアノテーションを持つロギングインターセプターと @Traced バインディングアノテーションを持つトレースインターセプターがあるとします。

@ApplicationScoped
@Logged
public class MyService {
    public void doSomething() {
        ...
    }

    @Traced
    public void doSomethingElse() {
        ...
    }
}

この例では、 doSomethingdoSomethingElse の両方が架空のロギングインターセプターによってインターセプトされます。さらに、 doSomethingElse メソッドは、架空のトレースインターセプターによってインターセプトされます。

もし @Traced インターセプターが必要なロギングもすべて行っていた場合、このメソッドでは @Logged インターセプターを省略したいのですが、他のすべてのメソッドではそのままにしておきたいと思います。これを実現するには、メソッドに @NoClassInterceptors というアノテーションを付けます。

@Traced
@NoClassInterceptors
public void doSomethingElse() {
    ...
}

@NoClassInterceptors アノテーションはメソッドとコンストラクターに付けることができ、これらのメソッドとコンストラクターではすべてのクラスレベルのインターセプターが無視されることを意味します。つまり、メソッド/コンストラクターに @NoClassInterceptors アノテーションが付けられている場合、このメソッド/コンストラクターに適用されるインターセプターは、メソッド/コンストラクターで直接宣言されたインターセプターのみです。

このアノテーションは、ビジネスメソッドインターセプター (@AroundInvoke) とコンストラクターライフサイクルコールバックインターセプター (@AroundConstruct) にのみ影響します。

4.20. 非同期オブザーバーメソッドによって出力される例外

非同期オブザーバーによって例外が発生した場合、 fireAsync() メソッドによって返される CompletionStage は例外的に完了し、イベントプロデューサーが適切に対応できるようになります。 ただし、イベントプロデューサーが気にしない場合は、例外は黙って無視されます。 したがって、Quarkus はデフォルトでエラーメッセージをログに記録します。 カスタムの AsyncObserverExceptionHandler を実装することも可能です。 このインターフェイスを実装する Bean は、 @jakarta.inject.Singleton または @jakarta.enterprise.context.ApplicationScoped である必要があります。

NoopAsyncObserverExceptionHandler
@Singleton
public class NoopAsyncObserverExceptionHandler implements AsyncObserverExceptionHandler {

  void handle(Throwable throwable, ObserverMethod<?> observerMethod, EventContext<?> eventContext) {
    // do nothing
  }

}

4.21. インターセプトされた自己呼び出し

Quarkus は、インターセプトされた自己呼び出し (または単に自己インターセプションと呼ぶ) をサポートしています。これは、CDI Bean が、関連付けられているインターセプターをトリガーしながら、別のメソッド内から独自のインターセプトされたメソッドを呼び出すシナリオです。 CDI 仕様では自己インターセプトが機能するかどうかが定義されていないため、これは非標準の機能です。

メソッドが 2 つある CDI Bean があり、そのうちの 1 つに @Transactional インターセプターバインディングが関連付けられているとします。

@ApplicationScoped
public class MyService {

  @Transactional (1)
  void doSomething() {
    // some application logic
  }

  void doSomethingElse() {
    doSomething();(2)
  }

}
1 1 つ以上のインターセプターバインディング。 @Transactional は単なる例です。
2 インターセプトされないメソッドが、関連付けられたバインディングを持つ同じ Bean から別のメソッドを呼び出します。これにより、インターセプトがトリガーされます。

上記の例では、 doSomething() メソッドを呼び出すすべてのコードがインターセプションをトリガーします。この場合、メソッドはトランザクションになります。 これは、呼び出しが MyService Bean (MyService#doSomethingElse など) から直接発生したか、他の Bean から発生したかには関係ありません。

4.22. インターセプトプロデューサーメソッドと合成 Bean

デフォルトでは、インターセプションはマネージド Bean (クラスベース Bean とも呼ばれます) に対してのみサポートされます。 プロデューサーメソッドと合成 Bean のインターセプションをサポートするために、CDI 仕様には InterceptionFactory が含まれていますが、これはランタイム指向の概念であるため、Quarkus ではサポートできません。

代わりに、Quarkus には独自の API: InterceptionProxy@BindingsSource があります。 InterceptionProxyInterceptionFactory と非常によく似ており、メソッド呼び出しをターゲットインスタンスに転送する前に @AroundInvoke インターセプターを適用するプロキシーを作成します。 @BindingsSource アノテーションを使用すると、インターセプトされたクラスが外部にあり、変更できない場合に、インターセプターバインディングを設定できます。

import io.quarkus.arc.InterceptionProxy;

@ApplicationScoped
class MyProducer {
    @Produces
    MyClass produce(InterceptionProxy<MyClass> proxy) { (1)
        return proxy.create(new MyClass()); (2)
    }
}
1 InterceptionProxy<MyClass> 型の注入ポイントを宣言します。 つまり、ビルド時に、インターセプトと転送を行う MyClass のサブクラスが生成されます。 型引数はプロデューサーメソッドの戻り値の型と同じである必要があることに注意してください。
2 指定された MyClass インスタンスのインターセプトプロキシーのインスタンスを作成します。 すべてのインターセプターが実行された後、メソッド呼び出しはこのターゲットインスタンスに転送されます。

この例では、インターセプターバインディングは MyClass クラスから読み取られます。

InterceptionProxy は、インターセプタークラスで宣言された @AroundInvoke インターセプターのみをサポートすることに注意してください。 他の種類のインターセプション、およびターゲットクラスとそのスーパークラスで宣言された @AroundInvoke インターセプターはサポートされていません。

インターセプトされたクラスは https://jakarta.ee/specifications/cdi/4.1/jakarta -cdi-spec-4.1#unproxyable[proxyable] である必要があるため、 final や、プライベート以外の final メソッドを指定できません。また、プライベート以外のゼロパラメーターのコンストラクターを指定する必要があります。 そうでなく、enabled の場合は、バイトコード変換によって修正を試みます。ゼロパラメーターのコンストラクターを追加できない場合もあることに注意してください。

生成されたクラスは外部ライブラリーからのものであり、インターセプターバインディングアノテーションがまったく含まれていないことがよくあります。 このようなケースをサポートするには、 InterceptionProxy パラメーターで @BindingsSource アノテーションを宣言します。

import io.quarkus.arc.BindingsSource;
import io.quarkus.arc.InterceptionProxy;

abstract class MyClassBindings { (1)
    @MyInterceptorBinding
    abstract String doSomething();
}

@ApplicationScoped
class MyProducer {
    @Produces
    MyClass produce(@BindingsSource(MyClassBindings.class) InterceptionProxy<MyClass> proxy) { (2)
        return proxy.create(new MyClass());
    }
}
1 MyClass 構造をミラーリングし、インターセプターバインディングを含むクラス。
2 @BindingsSource アノテーションによると、 MyClass のインターセプターバインディングは MyClassBindings から読み取る必要があります。

bindings source の概念は、ビルド時に使いやすい InterceptionFactory.configure() と同等です。

プロデューサーメソッドインターセプションと合成 Bean インターセプションは、インスタンスメソッドに対してのみ機能します。 静的メソッドのインターセプション はプロデューサーメソッドと合成 Bean ではサポートされていません。

4.22.1. @BindingsSource の宣言

@BindingsSource アノテーションは、インターセプトされたクラスの構造を反映するクラスを指定します。 その後、インターセプターバインディングはそのクラスから読み取られ、インターセプトされたクラスで宣言されているかのように扱われます。

具体的には、バインディングソースクラスで宣言されたクラスレベルのインターセプターバインディングは、インターセプトされたクラスのクラスレベルのバインディングとして扱われます。 バインディングソースクラスで宣言されたメソッドレベルのインターセプターバインディングは、インターセプトされたクラスと同じ名前、戻り値の型、パラメーターの型、および static フラグを持つメソッドのメソッドレベルのバインディングとして扱われます。

メソッド本体を記述しなくて済むように、バインディングソースクラスとメソッドを abstract にするのが一般的です。

abstract class MyClassBindings {
    @MyInterceptorBinding
    abstract String doSomething();
}

このクラスはインスタンス化されることはなく、そのメソッドが呼び出されることもないので、これで問題ありませんが、非 abstract クラスを作成することも可能です。

class MyClassBindings {
    @MyInterceptorBinding
    String doSomething() {
        return null; (1)
    }
}
1 メソッド本体は重要ではありません。

ジェネリッククラスの場合、型変数名も同一である必要があることに注意してください。 たとえば、次のクラスの場合:

class MyClass<T> {
    T doSomething() {
        ...
    }

    void doSomethingElse(T param) {
        ...
    }
}

バインディングソースクラスでは、型変数の名前として T も使用する必要があります。

abstract class MyClassBindings<T> {
    @MyInterceptorBinding
    abstract T doSomething();
}

インターセプトされたクラスに存在するという理由だけで、アノテーションが付けられていないメソッドを宣言する必要はありません。 メソッドのサブセットにメソッドレベルのバインディングを追加する場合は、インターセプターバインディングを指定する予定のメソッドを宣言するだけで済みます。 クラスレベルのバインディングのみを追加する場合は、メソッドを宣言する必要はありません。

バインディングソースクラスには次のアノテーションを配置できます。

  • インターセプターバインディング: クラスとメソッド

  • ステレオタイプ: クラスについて

  • @NoClassInterceptors: メソッドについて

バインディングソースクラスに存在するその他のアノテーションは無視されます。

4.22.2. 合成 Bean

合成 Bean で InterceptionProxy を使用する場合も同様です。

まず、合成 Bean が InterceptionProxy を注入することを宣言する必要があります。

public void register(RegistrationContext context) {
    context.configure(MyClass.class)
            .types(MyClass.class)
            .injectInterceptionProxy() (1)
            .creator(MyClassCreator.class)
            .done();
}
1 つまり、これはビルド時に、インターセプトと転送を行う MyClass のサブクラスが生成されることを意味します。

次に、 BeanCreatorSyntheticCreationalContext から InterceptionProxy を取得して使用する必要があります。

public MyClass create(SyntheticCreationalContext<MyClass> context) {
    InterceptionProxy<MyClass> proxy = context.getInterceptionProxy(); (1)
    return proxy.create(new MyClass());
}
1 上記で宣言したように、 MyClassInterceptionProxy を取得します。 TypeLiteral を渡して getInjectedReference() メソッドを使用することも可能ですが、 getInterceptionProxy() の方が簡単です。

@BindingsSource に相当するものもあります。 injectInterceptionProxy() メソッドには、パラメーターが 1 つ指定されたオーバーロードがあります。

public void register(RegistrationContext context) {
    context.configure(MyClass.class)
            .types(MyClass.class)
            .injectInterceptionProxy(MyClassBindings.class) (1)
            .creator(MyClassCreator.class)
            .done();
}
1 引数はバインディングソースクラスです。

4.23. Instance.Handle.close() の動作

CDI 仕様によれば、 Instance.Handle.close() メソッドは常に destroy() に委任します。 ArC では、これは ストリクトモード では True のみです。

デフォルトモードでは、Bean が @Dependent の場合 (またはインスタンスハンドルが CDI コンテキストオブジェクトを表していない場合)、 close() メソッドは destroy() に委任するだけです。 インスタンスハンドルが他のスコープの Bean を表す場合、 close() メソッドは何も行いません。Bean はそのまま残され、コンテキストが破棄されると、Bean も破棄されます。

これは、次のコードを単純に期待どおりに動作させるためです。

Instance<T> instance = ...;
try (Instance.Handle<T> handle : instance.getHandle()) {
   T value = handle.get();
   ... use value ...
}

@Dependent Bean はすぐに破棄されますが、他の Bean は破棄されません。 これは、 Instance によって異なるスコープの複数の Bean が返される可能性がある場合に重要です。

5. リアクティブプログラミングの落とし穴

CDI は単なる同期フレームワークです。 非同期の概念は非常に限られており、スレッドプールとスレッドオフロードのみに基づいています。 したがって、CDI をリアクティブプログラミングと組み合わせて使用すると、いくつかの落とし穴が生じます。

5.1. ブロックが許可されているかどうかの検出

io.quarkus.runtime.BlockingOperationControl#isBlockingAllowed() メソッドを使用すると、現在のスレッドでブロッキングが許可されているかどうかを検出できます。 そうでない場合、ブロッキング操作を実行する必要がある場合は、その操作を別のスレッドにオフロードする必要があります。 最も簡単な方法は、 Vertx.executeBlocking() メソッドを使用することです。

import io.quarkus.runtime.BlockingOperationControl;

@ApplicationScoped
public class MyBean {
    @Inject
    Vertx vertx;

    @PostConstruct
    void init() {
        if (BlockingOperationControl.isBlockingAllowed()) {
            somethingThatBlocks();
        } else {
            vertx.executeBlocking(() -> {
                somethingThatBlocks();
                return null;
            });
        }
    }

    void somethingThatBlocks() {
        // use the file system or JDBC, call a REST service, etc.
        Thread.sleep(5000);
    }
}

5.2. 非同期オブザーバー

CDI 非同期オブザーバー (@ObservesAsync) はリアクティブプログラミングを認識しないため、リアクティブパイプラインの一部として使用できません。 オブザーバーメソッドは同期されることを意図しており、スレッドプールにオフロードされるだけです。

Event.fireAsync() メソッドは、すべてのオブザーバーに通知されると完了する CompletionStage を返します。 すべてのオブザーバーに正常に通知された場合、 CompletionStage はイベントペイロードで完了します。 一部のオブザーバーで例外が発生した場合、 CompletionStageCompletionException で例外的に完了します。

オブザーバーの戻り値の型は重要ではありません。 オブザーバーの戻り値は無視されます。

戻り値の型が CompletionStage<> または Uni<> であるオブザーバーメソッドを宣言できますが、戻り値の型も実際の戻り値も Event.fireAsync() の結果には影響しません。 さらに、オブザーバーが戻り値の型として Uni<> を宣言した場合、返された Uni はサブスクライブされないため、オブザーバーロジックの一部が実行されない可能性も十分にあります。

したがって、同期と非同期の両方のオブザーバーメソッドは常に void として宣言することを推奨します。

6. ビルド時エクステンション

Quarkus は、インスタント起動と低メモリフットプリントを提供するために、ビルド時の最適化を取り入れています。このアプローチの欠点は、CDI ポータブル・エクステンションをサポートできないことです。それにもかかわらず、ほとんどの機能は、Quarkus エクステンション を使用して行うことができます。詳細は、integration guide を参照してください。

7. 開発モード

開発モードでは、JSON 形式で基本的なデバッグ情報を提供するために、2 つの特別なエンドポイントが自動的に登録されます。

  • HTTP GET /q/arc - 要約、Bean の数、設定プロパティーなどを返します。

  • HTTP GET /q/arc/beans - すべての Bean のリストを返します。

  • HTTP GET /q/arc/removed-beans - ビルド中に削除された未使用の Bean のリストを返します。

  • HTTP GET /q/arc/observers - すべてのオブザーバーメソッドのリストを返します。

これらのエンドポイントは、開発モード、つまり mvn quarkus:dev (または ./gradlew quarkusDev) 経由でアプリケーションを実行する場合にのみ使用できます。

7.1. ビジネスメソッドの呼び出しとイベントの監視

開発モードでは、ビジネスメソッドの呼び出しと発生したイベントの監視を有効にすることもできます。 quarkus.arc.dev-mode.monitoring-enabled 設定プロパティーを true に設定し、関連する Dev UI ページを確認するだけです。

8. ストリクトモード

デフォルトでは、ArC は CDI 仕様で要求される全てのバリデーションを実行するわけではありません。また、多くの方法でCDIの使い勝手を向上させますが、その中には仕様に直接反するものもあります。

CDI Lite TCK に合格させるために、ArC には strict モードもあります。 このモードでは、追加の検証が有効になり、仕様と競合する特定の改善機能が無効になります。

ストリクトモードを有効にするには、次のように設定します:

quarkus.arc.strict-compatibility=true

その他にも、仕様の互換性に影響を与える機能があります:

仕様に近い動作を得るには、これらの機能を無効にする必要があります。

アプリケーションは、CDIをより便利に使えるように、デフォルトの非厳密モードを使用することが推奨されます。厳密モードの「厳密さ」(CDI仕様の上に追加される検証のセットと無効にされる改良のセット)は、将来変化する可能性があります。

9. ArC Configuration Reference

ビルド時に固定された設定プロパティー。その他の設定プロパティーは、すべて実行時にオーバーライド可能です。

Configuration property

デフォルト

  • If set to all (or true) the container will attempt to remove all unused beans.

  • If set to none (or false) no beans will ever be removed even if they are unused (according to the criteria set out below)

  • If set to fwk, then all unused beans will be removed, except the unused beans whose classes are declared in the application code

An unused bean:

  • is not a built-in bean or interceptor,

  • is not eligible for injection to any injection point,

  • is not excluded by any extension,

  • does not have a name,

  • does not declare an observer,

  • does not declare any producer which is eligible for injection to any injection point,

  • is not directly eligible for injection into any jakarta.enterprise.inject.Instance injection point

Environment variable: QUARKUS_ARC_REMOVE_UNUSED_BEANS

Show more

string

all

If set to true @Inject is automatically added to all non-static non-final fields that are annotated with one of the annotations defined by AutoInjectAnnotationBuildItem.

Environment variable: QUARKUS_ARC_AUTO_INJECT_FIELDS

Show more

boolean

true

If set to true, the bytecode of unproxyable beans will be transformed. This ensures that a proxy/subclass can be created properly. If the value is set to false, then an exception is thrown at build time indicating that a subclass/proxy could not be created. Quarkus performs the following transformations when this setting is enabled:

  • Remove 'final' modifier from classes and methods when a proxy is required.

  • Create a no-args constructor if needed.

  • Makes private no-args constructors package-private if necessary.

Environment variable: QUARKUS_ARC_TRANSFORM_UNPROXYABLE_CLASSES

Show more

boolean

true

If set to true, the bytecode of private fields that are injection points will be transformed to package private. This ensures that field injection can be performed completely reflection-free. If the value is set to false, then a reflection fallback is used to perform the injection.

Environment variable: QUARKUS_ARC_TRANSFORM_PRIVATE_INJECTED_FIELDS

Show more

boolean

true

If set to true (the default), the build fails if a private method that is neither an observer nor a producer, is annotated with an interceptor binding. An example of this is the use of Transactional on a private method of a bean. If set to false, Quarkus simply logs a warning that the annotation will be ignored.

Environment variable: QUARKUS_ARC_FAIL_ON_INTERCEPTED_PRIVATE_METHOD

Show more

boolean

true

The list of selected alternatives for an application.

An element value can be:

  • a fully qualified class name, i.e. org.acme.Foo

  • a simple class name as defined by Class#getSimpleName(), i.e. Foo

  • a package name with suffix .*, i.e. org.acme.*, matches a package

  • a package name with suffix .**, i.e. org.acme.**, matches a package that starts with the value

Each element value is used to match an alternative bean class, an alternative stereotype annotation type or a bean class that declares an alternative producer. If any value matches then the priority of Integer#MAX_VALUE is used for the relevant bean. The priority declared via jakarta.annotation.Priority is overridden.

Environment variable: QUARKUS_ARC_SELECTED_ALTERNATIVES

Show more

list of string

If set to true then jakarta.enterprise.inject.Produces is automatically added to all non-void methods that are annotated with a scope annotation, a stereotype or a qualifier, and are not annotated with Inject or Produces, and no parameter is annotated with Disposes, Observes or ObservesAsync.

Environment variable: QUARKUS_ARC_AUTO_PRODUCER_METHODS

Show more

boolean

true

The list of types that should be excluded from discovery.

An element value can be:

  • a fully qualified class name, i.e. org.acme.Foo

  • a simple class name as defined by Class#getSimpleName(), i.e. Foo

  • a package name with suffix .*, i.e. org.acme.*, matches a package

  • a package name with suffix .**, i.e. org.acme.**, matches a package that starts with the value

If any element value matches a discovered type then the type is excluded from discovery, i.e. no beans and observer methods are created from this type.

Environment variable: QUARKUS_ARC_EXCLUDE_TYPES

Show more

list of string

List of types that should be considered unremovable regardless of whether they are directly used or not. This is a configuration option equivalent to using io.quarkus.arc.Unremovable annotation.

An element value can be:

  • a fully qualified class name, i.e. org.acme.Foo

  • a simple class name as defined by Class#getSimpleName(), i.e. Foo

  • a package name with suffix .*, i.e. org.acme.*, matches a package

  • a package name with suffix .**, i.e. org.acme.**, matches a package that starts with the value

If any element value matches a discovered bean, then such a bean is considered unremovable.

Environment variable: QUARKUS_ARC_UNREMOVABLE_TYPES

Show more

list of string

Artifacts that should be excluded from discovery

デフォルト

The maven groupId of the artifact.

Environment variable: QUARKUS_ARC_EXCLUDE_DEPENDENCY__DEPENDENCY_NAME__GROUP_ID

Show more

string

required

The maven artifactId of the artifact (optional).

Environment variable: QUARKUS_ARC_EXCLUDE_DEPENDENCY__DEPENDENCY_NAME__ARTIFACT_ID

Show more

string

The maven classifier of the artifact (optional).

Environment variable: QUARKUS_ARC_EXCLUDE_DEPENDENCY__DEPENDENCY_NAME__CLASSIFIER

Show more

string

If set to true then the container attempts to detect "unused removed beans" false positives during programmatic lookup at runtime. You can disable this feature to conserve some memory when running your application in production.

Environment variable: QUARKUS_ARC_DETECT_UNUSED_FALSE_POSITIVES

Show more

boolean

true

If set to true then the container attempts to detect wrong usages of annotations and eventually fails the build to prevent unexpected behavior of a Quarkus application.

A typical example is @jakarta.ejb.Singleton which is often confused with @jakarta.inject.Singleton. As a result a component annotated with @jakarta.ejb.Singleton would be completely ignored. Another example is an inner class annotated with a scope annotation - this component would be again completely ignored.

Environment variable: QUARKUS_ARC_DETECT_WRONG_ANNOTATIONS

Show more

boolean

true

If set to true, the container will perform additional validations mandated by the CDI specification. Some improvements on top of the CDI specification may be disabled. Applications that work as expected in the strict mode should work without a change in the default, non-strict mode.

The strict mode is mainly introduced to allow passing the CDI Lite TCK. Applications are recommended to use the default, non-strict mode, which makes CDI more convenient to use. The "strictness" of the strict mode (the set of additional validations and the set of disabled improvements on top of the CDI specification) may change over time.

Note that transform-unproxyable-classes and remove-unused-beans also has effect on specification compatibility. You may want to disable these features to get behavior closer to the specification.

Environment variable: QUARKUS_ARC_STRICT_COMPATIBILITY

Show more

boolean

false

If set to true then the container monitors business method invocations and fired events during the development mode.

This config property should not be changed in the development mode as it requires a full rebuild of the application

Environment variable: QUARKUS_ARC_DEV_MODE_MONITORING_ENABLED

Show more

boolean

false

If set to true then the dependency graphs are generated and available in the Dev UI.

Environment variable: QUARKUS_ARC_DEV_MODE_GENERATE_DEPENDENCY_GRAPHS

Show more

boolean

true

If set to true then disable StartupEvent and ShutdownEvent observers declared on application bean classes during the tests.

Environment variable: QUARKUS_ARC_TEST_DISABLE_APPLICATION_LIFECYCLE_OBSERVERS

Show more

boolean

false

The list of packages that will not be checked for split package issues.

A package string representation can be:

  • a full name of the package, i.e. org.acme.foo

  • a package name with suffix .*, i.e. org.acme.*, which matches a package that starts with provided value

Environment variable: QUARKUS_ARC_IGNORED_SPLIT_PACKAGES

Show more

list of string

If set to true and the SmallRye Context Propagation extension is present then the CDI contexts will be propagated by means of the MicroProfile Context Propagation API. Specifically, a org.eclipse.microprofile.context.spi.ThreadContextProvider implementation is registered. On the other hand, if set to false then the MicroProfile Context Propagation API will never be used to propagate the CDI contexts. Note that the CDI contexts may be propagated in a different way though. For example with the Vertx duplicated context.

Environment variable: QUARKUS_ARC_CONTEXT_PROPAGATION_ENABLED

Show more

boolean

true

関連コンテンツ