The English version of quarkus.io is the official project site. Translated sites are community supported on a best-effort basis.

CDI 統合ガイド

QuarkusのCDIコンテナであるArCは、ビルド時にブートストラップされます。 コンテナと統合するには、Quarkus固有のエクステンションAPIだけでなく、 CDIビルド互換エクステンション も使用できます。 CDI Portable Extensionsはサポートされていません。 このガイドでは、Quarkus固有のエクステンションAPIを中心に説明します。

コンテナーは複数のフェーズでブートストラップされます。高レベルの視点から見ると、これらのフェーズは以下のようになります。

  1. 初期化

  2. Beanディスカバリ

  3. 合成コンポーネントの登録

  4. バリデーション

初期化 フェーズでは、準備作業が行われ、カスタムコンテキストが登録されます。その後、コンテナーがすべてのアプリケーションクラスを分析し、Beanを識別し、提供されたメタデータに基づいてそれらをすべて繋ぎ合わせるプロセスがBean ディスカバリ です。その後、エクステンションは 合成コンポーネント を登録することができます。これらのコンポーネントの属性はエクステンションによって完全に制御されます。最後に、 デプロイメントが検証されます 。例えば、コンテナーはアプリケーション内のすべての注入ポイントを検証し、与えられた必要な型と修飾子を満たすBeanがない場合はビルドを失敗させます。

You can see more information about the bootstrap by enabling additional logging. Simply run the Maven build with -X or --debug and grep the lines that contain io.quarkus.arc. In dev mode, you can use quarkus.log.category."io.quarkus.arc.processor".level=DEBUG and two special endpoints are also registered automatically to provide some basic debug info in the JSON format.

Quarkusのビルドステップでは、さまざまなビルドアイテムを生成したり消費したりして、各フェーズにフックすることができます。以下のセクションでは、関連するすべてのビルド項目と一般的なシナリオについて説明します。

1. メタデータソース

クラスとアノテーションは、Beanレベルのメタデータの主要なソースです。初期のメタデータは、Beanの ディスカバリ 時に様々なソースから構築される不変の Jandexインデックス である _Beanアーカイブインデックス から読み込まれます。しかし、エクステンションは、ブートストラップの特定の段階でメタデータを追加、削除、変換することができます。さらに、エクステンションは 合成コンポーネント を登録することもできます。これは、CDIコンポーネントをQuarkusに統合する際に実現すべき重要な側面です。

このようにして、エクステンションは、そうでなければ無視されていたクラスをBeanに変えたり、その逆を行ったりすることができます。例えば、 @Scheduled メソッドを宣言するクラスは、たとえそれがBean定義アノテーションでアノテーションされておらず、通常は無視されるようなクラスであっても、常にBeanとして登録されます。

2. ユースケース - クラスがBeanとして認識されません

UnsatisfiedResolutionException は、 タイプセーフ解決 時に問題があることを示しています。クラスパス上にインジェクションが可能なクラスがあっても、インジェクションポイントを満たすことができないことがあります。クラスが認識されない理由はいくつかありますが、それを解決する方法もいくつかあります。最初のステップでは、その 理由 を特定する必要があります。

2.1. 理由1 :クラスが発見されない

Quarkusには 簡易ディスカバリー があります。クラスがアプリケーションのインデックスに含まれていないことが起こるかもしれません。例えば、Quarkusエクステンションの ランタイムモジュール のクラスは自動的にインデックス化されません。

解決策AdditionalBeanBuildItem .このビルド項目は、ディスカバリー中に解析する1つ以上の追加クラスを指定するために使用することができます。追加のBean・クラスは、コンテナーによって処理されるアプリケーション・インデックスに透過的に追加されます。

cdi-reference および cdi-reference で説明されているように、 @IfBuildProfile, @UnlessBuildProfile, @IfBuildProperty および @UnlessBuildProperty アノテーションを介して、条件付きで追加のBeanを有効化/無効化することはできません。エクステンションは、設定または現在のプロファイルを検査し、本当に必要な場合にのみ AdditionalBeanBuildItem を生成するべきです。
AdditionalBeanBuildItem の例
@BuildStep
AdditionalBeanBuildItem additionalBeans() {
     return new AdditionalBeanBuildItem(SmallRyeHealthReporter.class, HealthServlet.class)); (1)
}
1 AdditionalBeanBuildItem.Builder は、より複雑なユースケースに使用することができます。

AdditionalBeanBuildItem 経由で追加された Bean クラスは、デフォルトでは 取り外し可能です 。コンテナーがそれらを 未使用 とみなした場合、それらはただ無視されます。しかし、 AdditionalBeanBuildItem.Builder.setUnremovable() メソッドを使用して、このビルド項目を介して登録されたBeanクラスを絶対に削除しないようにコンテナーに指示することができます。詳細は、未使用のBeanの 削除[unremovable_builditem] も参照してください。

AdditionalBeanBuildItem.Builder#setDefaultScope() からデフォルトのスコープを設定することも可能です。デフォルトのスコープは、Beanクラスにスコープが宣言されていない場合にのみ使用されます。

デフォルトスコープが指定されていない場合は @Dependent 擬似スコープが使用されます。

2.2. 理由2 : クラスは発見されたが、Beanを定義するアノテーションがない

In Quarkus, the application is represented by a single bean archive with the bean discovery mode annotated. Therefore, bean classes that don’t have a bean defining annotation are ignored. Bean defining annotations are declared on the class-level and include scopes, stereotypes and @Interceptor.

解決策1 : AutoAddScopeBuildItem の使用。このビルドアイテムを使用すると、特定の条件を満たすクラスにスコープを追加することができます。

AutoAddScopeBuildItem の例
@BuildStep
AutoAddScopeBuildItem autoAddScope() {
   return AutoAddScopeBuildItem.builder().containsAnnotations(SCHEDULED_NAME, SCHEDULES_NAME) (1)
      .defaultScope(BuiltinScope.SINGLETON) (2)
      .build();
}
1 @Scheduled でアノテーションされたすべてのクラスを検索
2 デフォルトのスコープとして @Singleton を追加。既にスコープでアノテーションされているクラスは自動的にスキップされます。

解決策2: 特定のアノテーションが付けられたクラスを処理する必要がある場合は、BeanDefiningAnnotationBuildItem を介して Bean 定義アノテーションのセットを拡張することができます。

BeanDefiningAnnotationBuildItem の例
@BuildStep
BeanDefiningAnnotationBuildItem additionalBeanDefiningAnnotation() {
   return new BeanDefiningAnnotationBuildItem(Annotations.GRAPHQL_API); (1)
}
1 Bean 定義アノテーションのセットに org.eclipse.microprofile.graphql.GraphQLApi を追加します。

BeanDefiningAnnotationBuildItem を介して追加された Bean クラスは、デフォルトでは 削除不可 です。したがって、結果の Bean は未使用と見なされても削除しないでください。ただし、デフォルトの動作は変更できます。詳細についてはRemoving Unused Beans および理由3: クラスが検出され、Bean 定義のアノテーションがあるが削除された を参照してください。

デフォルトスコープを設定することもできます。デフォルトスコープは、Bean クラスにスコープが宣言されていない場合にのみ使用されます。

デフォルトスコープが指定されていない場合は @Dependent 擬似スコープが使用されます。

2.3. 理由3: クラスが検出され、Bean 定義のアノテーションがあるが削除された

デフォルトで、コンテナーはビルド時に remove all unused beans を試行します。この最適化により、フレームワークレベルでのデッドコードの排除 が可能になります。いくつかの特殊なケースでは、未使用の Bean を正しく特定できません。特に、Quarkus はまだ CDI.current() 静的メソッドの使用を検出できません。エクステンションは、UnremovableBeanBuildItem を生成することで、誤検出の可能性をなくすことができます。

UnremovableBeanBuildItem の例
@BuildStep
UnremovableBeanBuildItem unremovableBeans() {
   return UnremovableBeanBuildItem.targetWithAnnotation(STARTUP_NAME); (1)
}
1 @Startup でアノテーションされたすべてのクラスを削除できないようにする。

3. ユースケース - アノテーションが修飾子またはインターセプターバインディングとして認識されない

アノテーションクラスがアプリケーションインデックスに含まれていない可能性があります。たとえば、Quarkus エクステンションの ランタイムモジュール のクラスは自動的にインデックス化されません。

解決策: 理由1 :クラスが発見されない で説明されているとおりに AdditionalBeanBuildItem を使用します。

4. ユースケース - アノテーションのメタデータを変換する必要がある場合

In some cases, it’s useful to be able to modify the annotation metadata. Quarkus provides a powerful alternative to jakarta.enterprise.inject.spi.ProcessAnnotatedType and jakarta.enterprise.inject.build.compatible.spi.Enhancement. With an AnnotationsTransformerBuildItem it’s possible to override the annotations that exist on bean classes.

アノテーショントランスフォーマーは、Beanディスカバリが始まる 前に 生成されなければならないことを覚えておいてください。

For example, you might want to add an interceptor binding to a specific bean class. You can use a convenient builder-like API to create a transformer instance:

Builder Example

@BuildStep
AnnotationsTransformerBuildItem transform() {
   return new AnnotationsTransformerBuildItem(AnnotationsTransformer.appliedToClass() (1)
        .whenClass(c -> c.name().toString().equals("org.acme.Bar")) (2)
        .thenTransform(t -> t.add(MyInterceptorBinding.class))); (3)
}
1 トランスフォーマーはクラスにのみ適用されます。
2 Only apply the transformation if the class name equals to org.acme.Bar.
3 @MyInterceptorBinding アノテーションを追加します。

The example above can be rewritten with an anonymous class:

AnnotationsTransformerBuildItem の例
@BuildStep
AnnotationsTransformerBuildItem transform() {
   return new AnnotationsTransformerBuildItem(new AnnotationsTransformer() {

      public boolean appliesTo(org.jboss.jandex.AnnotationTarget.Kind kind) {
         return kind == org.jboss.jandex.AnnotationTarget.Kind.CLASS; (1)
      }

      public void transform(TransformationContext context) {
         if (context.getTarget().asClass().name().toString().equals("org.acme.Bar")) {
            context.transform().add(MyInterceptorBinding.class).done(); (2)
         }
      }
    });
}
1 トランスフォーマーはクラスにのみ適用されます。
2 クラス名が org.acme.Bar と同じ場合は、@MyInterceptorBinding を追加します。Transformation#done() を呼び出すことを忘れないでください。

ビルドステップでは、TransformedAnnotationsBuildItem を介して、特定のアノテーションターゲットの変換済みアノテーションをクエリーできます。

TransformedAnnotationsBuildItem の例
@BuildStep
void queryAnnotations(TransformedAnnotationsBuildItem transformedAnnotations, BuildProducer<MyBuildItem> myBuildItem) {
   ClassInfo myClazz = ...;
   if (transformedAnnotations.getAnnotations(myClazz).isEmpty()) { (1)
     myBuildItem.produce(new MyBuildItem());
   }
}
1 TransformedAnnotationsBuildItem.getAnnotations() は、変換された可能性のあるアノテーションのセットを返します。

4.1. Annotation TransformerのTrace Loggingを有効にする方法

io.quarkus.arc.processor のカテゴリーに TRACE レベルを設定し、その後、ログ出力の解析を試みてください。

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

5. ユースケース - Bean、オブザーバー、インジェクションポイントの検査

5.1. 解決策1. BeanDiscoveryFinishedBuildItem

BeanDiscoveryFinishedBuildItem のコンシューマーは、アプリケーションに登録されているすべてのクラスベースの Bean、オブザーバー、およびインジェクションポイントを簡単に検査できます。ただし、このビルドアイテムは合成コンポーネントが登録される に作成されるため、合成 Bean とオブザーバーは 含まれません

さらに、BeanDiscoveryFinishedBuildItem#getBeanResolver() から返された Bean リゾルバーを使用して、タイプセーフな解決ルールを適用できます。たとえば、必要な型と修飾子の特定の組み合わせを満たす Bean があるかどうかを確認できます。

BeanDiscoveryFinishedBuildItem の例
@BuildStep
void doSomethingWithNamedBeans(BeanDiscoveryFinishedBuildItem beanDiscovery, BuildProducer<NamedBeansBuildItem> namedBeans) {
   List<BeanInfo> namedBeans = beanDiscovery.beanStream().withName().collect(toList())); (1)
   namedBeans.produce(new NamedBeansBuildItem(namedBeans));
}
1 結果のリストに @Named 合成 Bean は含まれません。

5.2. 解決策2 : SynthesisFinishedBuildItem

SynthesisFinishedBuildItem のコンシューマーは、アプリケーションに登録されているすべての Bean、オブザーバー、およびインジェクションポイントを簡単に検査できます。このビルドアイテムは合成コンポーネントが登録された に作成されるため、合成 Bean とオブザーバーも 含まれます

さらに、SynthesisFinishedBuildItem#getBeanResolver() から返された Bean リゾルバーを使用して、タイプセーフな解決ルールを適用できます。たとえば、必要な型と修飾子の特定の組み合わせを満たす Bean があるかどうかを確認できます。

SynthesisFinishedBuildItem の例
@BuildStep
void doSomethingWithNamedBeans(SynthesisFinishedBuildItem synthesisFinished, BuildProducer<NamedBeansBuildItem> namedBeans) {
   List<BeanInfo> namedBeans = synthesisFinished.beanStream().withName().collect(toList())); (1)
   namedBeans.produce(new NamedBeansBuildItem(namedBeans));
}
1 結果のリストには、@Named 合成 Bean が含まれます。

6. ユースケース - 合成 Bean の必要性

Sometimes it is practical to be able to register a synthetic bean. Bean attributes of a synthetic bean are not derived from a Java class, method or field. Instead, all the attributes are defined by an extension. In regular CDI, this could be achieved using the AfterBeanDiscovery.addBean() and SyntheticComponents.addBean() methods.

解決策: 合成 Bean を登録する必要がある場合は、SyntheticBeanBuildItem を使用します。

SyntheticBeanBuildItem の例 1
@BuildStep
SyntheticBeanBuildItem syntheticBean() {
   return SyntheticBeanBuildItem.configure(String.class)
             .qualifiers(AnnotationInstance.builder(MyQualifier.class).build())
             .creator(mc -> mc.returnValue(mc.load("foo"))) (1)
             .done();
}
1 Generate the bytecode of the jakarta.enterprise.context.spi.Contextual#create(CreationalContext<T>) implementation.

Bean Configurator の出力は、バイトコードとして記録されます。したがって、実行時に合成 Bean インスタンスを作成する方法にはいくつかの制限があります。以下が可能です。

  1. Contextual#create(CreationalContext<T>) メソッドのバイトコードを、ExtendedBeanConfigurator.creator(Consumer<MethodCreator>) を介して直接生成します。

  2. Pass a subclass of io.quarkus.arc.BeanCreator via ExtendedBeanConfigurator#creator(Class<? extends BeanCreator<U>>), and possibly specify some build-time parameters via ExtendedBeanConfigurator#param() and synthetic injection points via ExtendedBeanConfigurator#addInjectionPoint().

  3. Produce the runtime instance through a proxy returned from a @Recorder method and set it via ExtendedBeanConfigurator#runtimeValue(RuntimeValue<?>), ExtendedBeanConfigurator#runtimeProxy(Object), ExtendedBeanConfigurator#supplier(Supplier<?>) or ExtendedBeanConfigurator#createWith(Function<SyntheticCreationalContext<?>, <?>).

SyntheticBeanBuildItem の例 2
@BuildStep
@Record(STATIC_INIT) (1)
SyntheticBeanBuildItem syntheticBean(TestRecorder recorder) {
   return SyntheticBeanBuildItem.configure(Foo.class).scope(Singleton.class)
                .runtimeValue(recorder.createFoo()) (2)
                .done();
}
1 デフォルトでは、合成 Bean は STATIC_INIT の間に初期化されます。
2 Bean インスタンスは、レコーダーメソッドから返される値によって提供されます。

RUNTIME_INIT の間に初期化される合成 Bean をマークできます。STATIC_INITRUNTIME_INIT の違いの詳細については、Three Phases of Bootstrap and Quarkus Philosophy を参照してください。

RUNTIME_INIT SyntheticBeanBuildItem の例
@BuildStep
@Record(RUNTIME_INIT) (1)
SyntheticBeanBuildItem syntheticBean(TestRecorder recorder) {
   return SyntheticBeanBuildItem.configure(Foo.class).scope(Singleton.class)
                .setRuntimeInit() (2)
                .runtimeValue(recorder.createFoo())
                .done();
}
1 レコーダーは、ExecutionTime.RUNTIME_INIT フェーズで実行する必要があります。
2 Bean インスタンスは、RUNTIME_INIT の間に初期化されます。

RUNTIME_INIT の間に初期化された合成 Bean は、STATIC_INIT の間にアクセスしてはいけません。runtime-init 合成 Bean にアクセスする RUNTIME_INIT ビルドステップは、SyntheticBeansRuntimeInitBuildItem を消費します。

@BuildStep
@Record(RUNTIME_INIT)
@Consume(SyntheticBeansRuntimeInitBuildItem.class) (1)
void accessFoo(TestRecorder recorder) {
   recorder.foo(); (2)
}
1 このビルドステップは、syntheticBean() の完了後に実行する必要があります。
2 このレコーダーメソッドでは Foo Bean インスタンスが呼び出されるため、必ずすべての合成 Bean が初期化された後にビルドステップが実行されることを確認する必要があります。
BeanRegistrationPhaseBuildItem を使用して合成 Bean を登録することもできます。ただし、エクステンション作成者においては、Quarkus にとってより慣用的な SyntheticBeanBuildItem を使用することをお勧めします。

6.1. Synthetic Injection Points

A synthetic bean may register a synthetic injection point via the ExtendedBeanConfigurator#addInjectionPoint() method. This injection point is validated at build time and considered when detecting unused beans. The injected reference is accessible through the SyntheticCreationalContext#getInjectedReference() methods at runtime.

Synthetic Injection Point - Build Step Example
import org.jboss.jandex.ClassType;
import org.jboss.jandex.DotName;

@BuildStep
@Record(RUNTIME_INIT) (1)
SyntheticBeanBuildItem syntheticBean(TestRecorder recorder) {
   return SyntheticBeanBuildItem.configure(Foo.class)
                .scope(Singleton.class)
                .addInjectionPoint(ClassType.create(DotName.createSimple(Bar.class))) (2)
                .createWith(recorder.createFoo()) (3)
                .done();
}
1 Bean インスタンスは、RUNTIME_INIT の間に初期化されます。
2 A synthetic injection point with required type Bar was added; this is an equivalent of @Inject Bar.
3 The bean instance is created with a function returned from a recorder method.
Synthetic Injection Point - Recorder Example
@Recorder
public class TestRecorder {

   public Function<SyntheticCreationalContext<Foo>, Foo> createFoo() {
     return (context) -> {
        return new Foo(context.getInjectedReference(Bar.class)); (1)
     };
   }
}
1 Pass a contextual reference of Bar to the constructor of Foo.

7. ユースケース - 合成オブザーバー

synthetic beans と同様に、合成オブザーバーメソッドの属性は Java メソッドから派生しません。代わりに、すべての属性がエクステンションによって定義されます。

解決策: 合成オブザーバーを登録する必要がある場合は、ObserverRegistrationPhaseBuildItem を使用します。

ObserverRegistrationPhaseBuildItem を消費するビルドステップでは、常に ObserverConfiguratorBuildItem を生成するか、少なくともこのビルドアイテムに BuildProducer を 挿入する必要があります。でなければ、無視されるか、間違ったタイミングで処理される可能性があります (例: 正しい CDI ブートストラップフェーズの後)。
ObserverRegistrationPhaseBuildItem の例
@BuildStep
void syntheticObserver(ObserverRegistrationPhaseBuildItem observerRegistrationPhase,
            BuildProducer<MyBuildItem> myBuildItem,
            BuildProducer<ObserverConfiguratorBuildItem> observerConfigurationRegistry) {
   observerConfigurationRegistry.produce(new ObserverConfiguratorBuildItem(observerRegistrationPhase.getContext()
       .configure()
       .beanClass(DotName.createSimple(MyBuildStep.class.getName()))
       .observedType(String.class)
       .notify(mc -> {
           // do some gizmo bytecode generation...
       })));
   myBuildItem.produce(new MyBuildItem());
}

ObserverConfigurator の出力はバイトコードとして記録されます。したがって、実行時に合成オブザーバーを呼び出す方法にはいくつかの制限があります。現時点では、メソッド本体のバイトコードを直接生成する必要があります。

8. ユースケース - 生成された Bean クラスがある

問題ありません。Bean クラスのバイトコードを手動で生成し、その後に GeneratedClassBuildItem ではなく GeneratedBeanBuildItem を生成してください。

GeneratedBeanBuildItem の例
@BuildStep
void generatedBean(BuildProducer<GeneratedBeanBuildItem> generatedBeans) {
    ClassOutput beansClassOutput = new GeneratedBeanGizmoAdaptor(generatedBeans); (1)
    ClassCreator beanClassCreator = ClassCreator.builder().classOutput(beansClassOutput)
                .className("org.acme.MyBean")
                .build();
    beanClassCreator.addAnnotation(Singleton.class);
    beanClassCreator.close(); (2)
}
1 io.quarkus.arc.deployment.GeneratedBeanGizmoAdaptor を使用すると、Gizmo コンストラクトから GeneratedBeanBuildItem を簡単に作成できます。
2 結果の Bean クラスは、次のようになります: public class @Singleton MyBean { }

9. ユースケース - デプロイメントを検証する必要がある

エクステンションは、Bean、オブザーバー、およびインジェクションポイントを検査し、さらに追加の検証を実行して、何か問題がある場合はビルドを失敗にする必要があります。

解決策: エクステンションがデプロイメントを検証する必要がある場合、ValidationPhaseBuildItem を使用します。

ValidationPhaseBuildItem を消費するビルドステップでは、常に ValidationErrorBuildItem を生成するか、少なくともこのビルドアイテムに BuildProducer を 挿入する必要があります。でなければ、無視されるか、間違ったタイミングで処理される可能性があります (例: 正しい CDI ブートストラップフェーズの後)。
@BuildStep
void validate(ValidationPhaseBuildItem validationPhase,
            BuildProducer<MyBuildItem> myBuildItem,
            BuildProducer<ValidationErrorBuildItem> errors) {
   if (someCondition) {
     errors.produce(new ValidationErrorBuildItem(new IllegalStateException()));
     myBuildItem.produce(new MyBuildItem());
   }
}
ValidationPhaseBuildItem.getContext().beans() メソッドから返される便利な BeanStream を使用して、登録されているすべての Bean を簡単にフィルタリングできます。

10. ユースケース - カスタム CDI コンテキストの登録

時々、エクステンションは組み込みCDI コンテキストのセットを拡張する必要があります。

解決策: カスタムコンテキストを登録する必要がある場合は、ContextRegistrationPhaseBuildItem を使用します。

ContextRegistrationPhaseBuildItem を消費するビルドステップでは、常に ContextConfiguratorBuildItem を生成するか、少なくともこのビルドアイテムに `BuildProducer`を 挿入する必要があります。でなければ、無視されるか、間違ったタイミングで処理される可能性があります (例: 正しい CDI ブートストラップフェーズの後)。

ContextRegistrationPhaseBuildItem の例

@BuildStep
ContextConfiguratorBuildItem registerContext(ContextRegistrationPhaseBuildItem phase) {
      return new ContextConfiguratorBuildItem(phase.getContext().configure(TransactionScoped.class).normal().contextClass(TransactionContext.class));
}

さらに、ContextRegistrationPhaseBuildItem を介してカスタム CDI コンテキストを登録する各エクステンションは、Bean 定義アノテーションのセットにカスタムスコープアノテーション名を提供するために、CustomScopeBuildItem も生成する必要があります。

CustomScopeBuildItem の例

@BuildStep
CustomScopeBuildItem customScope() {
   return new CustomScopeBuildItem(DotName.createSimple(TransactionScoped.class.getName()));
}

10.1. アプリケーションで使用されているすべてのスコープを知る必要がある場合はどうなりますか?

ソリューション: ビルドステップで CustomScopeAnnotationsBuildItem を挿入し、 CustomScopeAnnotationsBuildItem.isScopeDeclaredOn() などの便利なメソッドを使用できます。

11. ユースケース - 追加のインターセプターバインディング

In rare cases it might be handy to programmatically register an existing annotation that is not annotated with @jakarta.interceptor.InterceptorBinding as an interceptor binding. This is similar to what CDI achieves through BeforeBeanDiscovery#addInterceptorBinding(). We are going to use InterceptorBindingRegistrarBuildItem to get it done.

InterceptorBindingRegistrarBuildItem の例
@BuildStep
InterceptorBindingRegistrarBuildItem addInterceptorBindings() {
    return new InterceptorBindingRegistrarBuildItem(new InterceptorBindingRegistrar() {
        @Override
        public List<InterceptorBinding> getAdditionalBindings() {
            return List.of(InterceptorBinding.of(NotAnInterceptorBinding.class));
        }
    });
}

12. 使用例 - 追加の修飾子

Sometimes it might be useful to register an existing annotation that is not annotated with @jakarta.inject.Qualifier as a CDI qualifier. This is similar to what CDI achieves through BeforeBeanDiscovery#addQualifier(). We are going to use QualifierRegistrarBuildItem to get it done.

QualifierRegistrarBuildItem の例
@BuildStep
QualifierRegistrarBuildItem addQualifiers() {
    return new QualifierRegistrarBuildItem(new QualifierRegistrar() {
        @Override
        public Map<DotName, Set<String>> getAdditionalQualifiers() {
            return Collections.singletonMap(DotName.createSimple(NotAQualifier.class.getName()),
                                        Collections.emptySet());
        }
    });
}

13. 使用例 - 追加のステレオタイプ

It is sometimes useful to register an existing annotation that is not annotated with @jakarta.enterprise.inject.Stereotype as a CDI stereotype. This is similar to what CDI achieves through BeforeBeanDiscovery#addStereotype(). We are going to use StereotypeRegistrarBuildItem to get it done.

StereotypeRegistrarBuildItem の例
@BuildStep
StereotypeRegistrarBuildItem addStereotypes() {
    return new StereotypeRegistrarBuildItem(new StereotypeRegistrar() {
        @Override
        public Set<DotName> getAdditionalStereotypes() {
            return Collections.singleton(DotName.createSimple(NotAStereotype.class.getName()));
        }
    });
}

新しく登録されたステレオタイプアノテーションに、スコープやインターセプターのバインディングなどの適切なメタアノテーションがない場合、 アノテーション変換 を使用してそれらを追加します。

14. ユースケース - インジェクションポイントの変換

プログラムでインジェクションポイントの修飾子を変更できると便利な場合があります。それは、InjectionPointTransformerBuildItem で実行できます。次のサンプルは、修飾子 MyQualifier を含むタイプ Foo のインジェクションポイント変換を適用する方法を示しています。

InjectionPointTransformerBuildItem の例
@BuildStep
InjectionPointTransformerBuildItem transformer() {
    return new InjectionPointTransformerBuildItem(new InjectionPointsTransformer() {

        public boolean appliesTo(Type requiredType) {
            return requiredType.name().equals(DotName.createSimple(Foo.class.getName()));
        }

        public void transform(TransformationContext context) {
            if (context.getQualifiers().stream()
                    .anyMatch(a -> a.name().equals(DotName.createSimple(MyQualifier.class.getName())))) {
                context.transform()
                        .removeAll()
                        .add(DotName.createSimple(MyOtherQualifier.class.getName()))
                        .done();
            }
        }
    });
}
理論的には、AnnotationsTransformer を使用して同じ目標を達成できます。ただし、次の多少の違いがあるため、このタスクには InjectionPointsTransformer の方が適しています。その違いとは次のとおりです。(1) アノテーショントランスフォーマーは Bean 検出中にすべてのクラスに適用されますが、InjectionPointsTransformer は Bean 検出後に検出されたインジェクションポイントにのみ適用されます。(2) InjectionPointsTransformer を使用すると、さまざまなタイプのインジェクションポイント (フィールド、初期化メソッドのパラメーターなど) を処理する必要がありません。

15. ユースケース - リソースアノテーションとインジェクション

ResourceAnnotationBuildItem を使用して、Jakarta EE リソースなどの非 CDI インジェクションポイントを解決できるリソースアノテーションを指定できます。インテグレーターは、対応する io.quarkus.arc.ResourceReferenceProvider サービスプロバイダー実装も提供する必要があります。

ResourceAnnotationBuildItem の例
@BuildStep
void setupResourceInjection(BuildProducer<ResourceAnnotationBuildItem> resourceAnnotations, BuildProducer<GeneratedResourceBuildItem> resources) {
    resources.produce(new GeneratedResourceBuildItem("META-INF/services/io.quarkus.arc.ResourceReferenceProvider",
        MyResourceReferenceProvider.class.getName().getBytes()));
    resourceAnnotations.produce(new ResourceAnnotationBuildItem(DotName.createSimple(MyAnnotation.class.getName())));
}

16. 利用可能なビルドタイムメタデータ

BuildExtension.BuildContext で動作する上記のエクステンションはいずれも、ビルドタイムに生成される特定のビルドタイムメタデータを利用することができます。 io.quarkus.arc.processor.BuildExtension.Key にある組込キーは以下の通りです。

ANNOTATION_STORE

アノテーショントランスフォーマーを適用した後は、すべての`AnnotationTarget`アノテーションに関する情報を保持する AnnotationStore が含まれます

INJECTION_POINTS

すべてのインジェクションポイントを含む Collection<InjectionPointInfo>

BEANS

すべての Bean を含む Collection<BeanInfo>

REMOVED_BEANS

Collection<BeanInfo> 削除されたすべての Bean を含む

OBSERVERS

すべてのオブザーバーを含む Collection<ObserverInfo>

SCOPES

カスタムスコープも含め、すべてのスコープを含む Collection<ScopeInfo>

QUALIFIERS

すべての修飾子を含む Map<DotName, ClassInfo>

INTERCEPTOR_BINDINGS

すべてのインターセプターバインディングを含む Map<DotName, ClassInfo>

STEREOTYPES

すべてのステレオタイプを含む Map<DotName, StereotypeInfo>

これらのメタデータを取得するには、そのキーのエクステンションコンテキストオブジェクトをクエリするだけ可能です。これらのメタデータはビルドが進むにつれて利用可能になることに注意してください。エクステンションがまだ生成されていないメタデータを取得しようとすると、 null が返されます。どのエクステンションがどのメタデータにアクセスできるかをまとめてみました。

AnnotationsTransformer

ブートストラップのどのフェーズでもいつでも使えるので、メタデータに頼るべきではありません。

ContextRegistrar

ANNOTATION_STORE , QUALIFIERS , INTERCEPTOR_BINDINGS . STEREOTYPES にアクセスできます。

InjectionPointsTransformer

ANNOTATION_STORE , QUALIFIERS , INTERCEPTOR_BINDINGS . STEREOTYPES にアクセスできます。

ObserverTransformer

ANNOTATION_STORE , QUALIFIERS , INTERCEPTOR_BINDINGS . STEREOTYPES にアクセスできます。

BeanRegistrar

ANNOTATION_STORE, QUALIFIERS, INTERCEPTOR_BINDINGS, STEREOTYPES, BEANS (クラスベースBeanのみ), OBSERVERS (クラスベースobserverのみ), INJECTION_POINTS にアクセスできます。

ObserverRegistrar

ANNOTATION_STORE , QUALIFIERS , INTERCEPTOR_BINDINGS , STEREOTYPES , BEANS , OBSERVERS (クラスベースのオブザーバーのみ), INJECTION_POINTS にアクセス可能です。

BeanDeploymentValidator

すべてのビルドメタデータにアクセスできます

関連コンテンツ