CDI Integration Guide
QuarkusのCDIコンテナであるArCは、ビルド時にブートストラップされます。 コンテナと統合するには、Quarkus固有のエクステンションAPIだけでなく、 CDIビルド互換エクステンション も使用できます。 CDI Portable Extensionsはサポートされていません。 このガイドでは、Quarkus固有のエクステンションAPIを中心に説明します。
コンテナーは複数のフェーズでブートストラップされます。高レベルの視点から見ると、これらのフェーズは以下のようになります。
-
初期化
-
Beanディスカバリ
-
合成コンポーネントの登録
-
バリデーション
初期化 フェーズでは、準備作業が行われ、カスタムコンテキストが登録されます。その後、コンテナーがすべてのアプリケーションクラスを分析し、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 the development 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 archive index から読み取られます。しかし、エクステンションは、ブートストラップの特定の段階でメタデータを追加、削除、変換することができます。さらに、エクステンションは 合成コンポーネント を登録することもできます。これは、QuarkusでCDIコンポーネントを統合する際に実現すべき重要な点です。
このようにして、エクステンションは、そうでなければ無視されていたクラスをBeanに変えたり、その逆を行ったりすることができます。例えば、 @Scheduled メソッドを宣言するクラスは、たとえそれがBean定義アノテーションでアノテーションされておらず、通常は無視されるようなクラスであっても、常にBeanとして登録されます。
2. ユースケース - クラスがBeanとして認識されません
UnsatisfiedResolutionException は、 タイプセーフな解決 中に問題が発生したことを示しています。クラスパス上に注入可能なクラスがあっても、注入ポイントを満たすことができないことがあります。クラスが認識されない理由はいくつかあり、またそれを解決する方法もいくつかあります。最初のステップでは、その 原因を 特定する必要があります。
2.1. Reason 1: Class Is Not discovered
Quarkusは 簡略化されたディスカバリー があります。クラスがアプリケーションのインデックスの一部でないことがあります。例えば、Quarkusエクステンションの 実行時モジュール のクラスは、自動的にインデックスされません。
解決策 。 AdditionalBeanBuildItem .このビルド項目は、ディスカバリー中に解析する1つ以上の追加クラスを指定するために使用することができます。追加のBean・クラスは、コンテナーによって処理されるアプリケーション・インデックスに透過的に追加されます。
It is not possible to conditionally enable/disable additional beans via the @IfBuildProfile, @UnlessBuildProfile, @IfBuildProperty and @UnlessBuildProperty annotations as described in Enabling Beans for Quarkus Build Profile and Enabling Beans for Quarkus Build Properties. Extensions should inspect the configuration or the current profile and only produce an AdditionalBeanBuildItem if really needed.
|
AdditionalBeanBuildItem Example@BuildStep
AdditionalBeanBuildItem additionalBeans() {
return new AdditionalBeanBuildItem(SmallRyeHealthReporter.class, HealthServlet.class)); (1)
}
| 1 | AdditionalBeanBuildItem.Builder は、より複雑なユースケースに使用することができます。 |
AdditionalBeanBuildItem を介して追加された Bean クラスは、デフォルトで 削除可能 です。コンテナがそれらを 未使用 とみなした場合、それらはただ無視されます。しかし, AdditionalBeanBuildItem.Builder.setUnremovable() メソッドを使用すると,このビルド項目を通じて登録された Bean クラスを決して削除しないようにコンテナに指示することができます。詳細は, 未使用のBeanの削除 及び 理由3:クラスが発見され,Bean定義アノテーションを有するが削除された も参照下さい。
AdditionalBeanBuildItem.Builder#setDefaultScope() からデフォルトのスコープを設定することも可能です。デフォルトのスコープは、Beanクラスにスコープが宣言されていない場合にのみ使用されます。
If no default scope is specified the @Dependent pseudo-scope is used.
|
2.2. Reason 2: Class Is Discovered but Has No Bean Defining Annotation
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 Example@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 Example@BuildStep
BeanDefiningAnnotationBuildItem additionalBeanDefiningAnnotation() {
return new BeanDefiningAnnotationBuildItem(Annotations.GRAPHQL_API); (1)
}
| 1 | Bean 定義アノテーションのセットに org.eclipse.microprofile.graphql.GraphQLApi を追加します。 |
Bean classes added via BeanDefiningAnnotationBuildItem are not removable by default, i.e. the resulting beans must not be removed even if they are considered unused.
However, you can change the default behavior.
See also Removing Unused Beans and Reason 3: Class Was Discovered and Has a Bean Defining Annotation but Was Removed for more details.
デフォルトスコープを設定することもできます。デフォルトスコープは、Bean クラスにスコープが宣言されていない場合にのみ使用されます。
If no default scope is specified the @Dependent pseudo-scope is used.
|
2.3. Reason 3: Class Was Discovered and Has a Bean Defining Annotation but Was Removed
コンテナは、デフォルトでビルド中に すべての未使用Beanを削除 しようとします。この最適化によって, フレームワークレベルのデッドコード除去が 可能になります。いくつかの特殊なケースでは、未使用のBeanを正しく識別することができません。特に、Quarkusでは、 CDI.current() 静的メソッドの使用をまだ検出できません。エクステンションは、 UnremovableBeanBuildItem を生成することで、誤検出の可能性を排除することができます。
UnremovableBeanBuildItem Example@BuildStep
UnremovableBeanBuildItem unremovableBeans() {
return UnremovableBeanBuildItem.targetWithAnnotation(STARTUP_NAME); (1)
}
| 1 | @Startup でアノテーションされたすべてのクラスを削除できないようにする。 |
3. ユースケース - アノテーションが修飾子またはインターセプターバインディングとして認識されない
アノテーションクラスがアプリケーションインデックスに含まれていない可能性があります。たとえば、Quarkus エクステンションの ランタイムモジュール のクラスは自動的にインデックス化されません。
Solution: Use the AdditionalBeanBuildItem as described in Reason 1: Class Is Not discovered.
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.
| Keep in mind that annotation transformers must be produced before the bean discovery starts. |
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:
ビルダーの例
@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 アノテーションを追加します。 |
上記の例は、匿名クラスを使用して書き直すことができます。
AnnotationsTransformerBuildItem Example@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 Example@BuildStep
void queryAnnotations(TransformedAnnotationsBuildItem transformedAnnotations, BuildProducer<MyBuildItem> myBuildItem) {
ClassInfo myClazz = ...;
if (transformedAnnotations.getAnnotations(myClazz).isEmpty()) { (1)
myBuildItem.produce(new MyBuildItem());
}
}
| 1 | TransformedAnnotationsBuildItem.getAnnotations() は、変換された可能性のあるアノテーションのセットを返します。 |
| There are other build items specialized in transformation: Use Case - Additional Interceptor Bindings and Use Case - Injection Point Transformation. |
4.1. Annotation TransformerのTrace Loggingを有効にする方法
io.quarkus.arc.processor のカテゴリーに TRACE レベルを設定し、その後、ログ出力の解析を試みてください。
application.properties Examplequarkus.log.category."io.quarkus.arc.processor".min-level=TRACE (1)
quarkus.log.category."io.quarkus.arc.processor".level=TRACE
| 1 | また、関連するカテゴリーの最小ログレベルを調整する必要があります。 |
5. ユースケース - Bean、オブザーバー、インジェクションポイントの検査
5.1. Solution 1: BeanDiscoveryFinishedBuildItem
BeanDiscoveryFinishedBuildItem のコンシューマーは、アプリケーションに登録されているすべてのクラスベースの Bean、オブザーバー、およびインジェクションポイントを簡単に検査できます。ただし、このビルドアイテムは合成コンポーネントが登録される 前 に作成されるため、合成 Bean とオブザーバーは 含まれません 。
さらに、 BeanDiscoveryFinishedBuildItem#getBeanResolver() から返された Bean リゾルバーを使用して、タイプセーフな解決ルールを適用できます。たとえば、必要な型と修飾子の特定の組み合わせを満たす Bean があるかどうかを確認できます。
BeanDiscoveryFinishedBuildItem Example@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. Solution 2: SynthesisFinishedBuildItem
SynthesisFinishedBuildItem のコンシューマーは、アプリケーションに登録されているすべての Bean、オブザーバー、およびインジェクションポイントを簡単に検査できます。このビルドアイテムは合成コンポーネントが登録された 後 に作成されるため、合成 Bean とオブザーバーも 含まれます 。
さらに、 SynthesisFinishedBuildItem#getBeanResolver() から返された Bean リゾルバーを使用して、タイプセーフな解決ルールを適用できます。たとえば、必要な型と修飾子の特定の組み合わせを満たす Bean があるかどうかを確認できます。
SynthesisFinishedBuildItem Example@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 Example 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 | jakarta.enterprise.context.spi.Contextual#create(CreationalContext<T>) 実装のバイトコードを生成します。 |
Bean Configurator の出力は、バイトコードとして記録されます。したがって、実行時に合成 Bean インスタンスを作成する方法にはいくつかの制限があります。以下が可能です。
-
Contextual#create(CreationalContext<T>)メソッドのバイトコードを、ExtendedBeanConfigurator.creator(Consumer<MethodCreator>)を介して直接生成します。 -
ExtendedBeanConfigurator#creator(Class<? extends BeanCreator<U>>)でio.quarkus.arc.BeanCreatorのサブクラスを渡し、場合によってはExtendedBeanConfigurator#param()でビルド時のパラメーターを、ExtendedBeanConfigurator#addInjectionPoint()で合成注入ポイントを指定します。 -
Produce the runtime instance through a proxy returned from a
@Recordermethod and set it viaExtendedBeanConfigurator#runtimeValue(RuntimeValue<?>),ExtendedBeanConfigurator#runtimeProxy(Object),ExtendedBeanConfigurator#supplier(Supplier<?>)orExtendedBeanConfigurator#createWith(Function<SyntheticCreationalContext<?>, <?>).
SyntheticBeanBuildItem Example 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 インスタンスは、レコーダーメソッドから返される値によって提供されます。 |
It is possible to mark a synthetic bean to be initialized during RUNTIME_INIT.
See the Three Phases of Bootstrap and Quarkus Philosophy for more information about the difference between STATIC_INIT and RUNTIME_INIT.
RUNTIME_INIT SyntheticBeanBuildItem Example@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 の間に初期化されます。 |
|
|
It is also possible to use the BeanRegistrationPhaseBuildItem to register a synthetic bean. However, we recommend extension authors to stick with SyntheticBeanBuildItem which is more idiomatic for Quarkus.
|
6.1. 合成注入ポイント
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.
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 | 必須の Bar 型の合成注入ポイントが追加されました。これは @Inject Bar と同じです。 |
| 3 | Bean インスタンスは、レコーダーメソッドから返される関数を使用して作成されます。 |
@Recorder
public class TestRecorder {
public Function<SyntheticCreationalContext<Foo>, Foo> createFoo() {
return (context) -> {
return new Foo(context.getInjectedReference(Bar.class)); (1)
};
}
}
| 1 | Bar のコンテキスト参照を Foo のコンストラクターに渡します。 |
7. ユースケース - 合成オブザーバー
Similar to synthetic beans, the attributes of a synthetic observer method are not derived from a Java method. Instead, all the attributes are defined by an extension.
解決策: 合成オブザーバーを登録する必要がある場合は、 ObserverRegistrationPhaseBuildItem を使用します。
A build step that consumes the ObserverRegistrationPhaseBuildItem should always produce an ObserverConfiguratorBuildItem or at least inject a BuildProducer for this build item, otherwise it could be ignored or processed at the wrong time (e.g. after the correct CDI bootstrap phase).
|
ObserverRegistrationPhaseBuildItem Example@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 Example@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 を使用します。
A build step that consumes the ValidationPhaseBuildItem should always produce a ValidationErrorBuildItem or at least inject a BuildProducer for this build item, otherwise it could be ignored or processed at the wrong time (e.g. after the correct CDI bootstrap phase).
|
@BuildStep
void validate(ValidationPhaseBuildItem validationPhase,
BuildProducer<MyBuildItem> myBuildItem,
BuildProducer<ValidationErrorBuildItem> errors) {
if (someCondition) {
errors.produce(new ValidationErrorBuildItem(new IllegalStateException()));
myBuildItem.produce(new MyBuildItem());
}
}
You can easily filter all registered beans via the convenient BeanStream returned from the ValidationPhaseBuildItem.getContext().beans() method.
|
10. ユースケース - カスタム CDI コンテキストの登録
時々、エクステンションは組み込みCDI コンテキストのセットを拡張する必要があります。
解決策: カスタムコンテキストを登録する必要がある場合は、 ContextRegistrationPhaseBuildItem を使用します。
A build step that consumes the ContextRegistrationPhaseBuildItem should always produce a ContextConfiguratorBuildItem or at least inject a BuildProducer for this build item, otherwise it could be ignored or processed at the wrong time (e.g. after the correct CDI bootstrap phase).
|
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()));
}
11. ユースケース - 追加のインターセプターバインディング
まれに、インターセプターバインディングとし て @jakarta.interceptor.InterceptorBinding でアノテーションが付けられていない既存のアノテーションをプログラムで登録すると便利な場合があります。これは、CDI が BeforeBeanDiscovery#addInterceptorBinding() で達成するものと似ています。これを行うには、 InterceptorBindingRegistrarBuildItem を使用します。
InterceptorBindingRegistrarBuildItem Example@BuildStep
InterceptorBindingRegistrarBuildItem addInterceptorBindings() {
return new InterceptorBindingRegistrarBuildItem(new InterceptorBindingRegistrar() {
@Override
public List<InterceptorBinding> getAdditionalBindings() {
return List.of(InterceptorBinding.of(NotAnInterceptorBinding.class));
}
});
}
12. 使用例 - 追加の修飾子
場合によっては、 @jakarta.inject.Qualifier でアノテーションされていない既存のアノテーションを CDI 修飾子として登録しておくと便利です。
これは CDI が BeforeBeanDiscovery#addQualifier() を通して実現していることに似ています。
ここでは QualifierRegistrarBuildItem を使ってそれを実現しようとしています。
QualifierRegistrarBuildItem Example@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. 使用例 - 追加のステレオタイプ
場合によっては、 @jakarta.enterprise.inject.Stereotype でアノテーションされていない既存のアノテーションを CDI ステレオタイプとして登録しておきます。
これは CDI が BeforeBeanDiscovery#addQualifier() を通して実現していることに似ています。ここでは QualifierRegistrarBuildItem を使ってそれを実現しようとしています。
StereotypeRegistrarBuildItem Example@BuildStep
StereotypeRegistrarBuildItem addStereotypes() {
return new StereotypeRegistrarBuildItem(new StereotypeRegistrar() {
@Override
public Set<DotName> getAdditionalStereotypes() {
return Collections.singleton(DotName.createSimple(NotAStereotype.class.getName()));
}
});
}
If the newly registered stereotype annotation doesn’t have the appropriate meta-annotations, such as scope or interceptor bindings, use an annotation transformation to add them.
14. ユースケース - インジェクションポイントの変換
プログラムでインジェクションポイントの修飾子を変更できると便利な場合があります。それは、 InjectionPointTransformerBuildItem で実行できます。次のサンプルは、修飾子 MyQualifier を含むタイプ Foo のインジェクションポイント変換を適用する方法を示しています。
InjectionPointTransformerBuildItem Example@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();
}
}
});
}
In theory, you can use an AnnotationsTransformer to achieve the same goal. However, there are few differences that make InjectionPointsTransformer more suitable for this particular task: (1) annotation transformers are applied to all classes during bean discovery, whereas InjectionPointsTransformer is only applied to discovered injection points after bean discovery; (2) with InjectionPointsTransformer you don’t need to handle various types of injection points (field, parameters of initializer methods, etc.).
|
15. ユースケース - リソースアノテーションとインジェクション
ResourceAnnotationBuildItem を使用して、Jakarta EE リソースなどの非 CDI インジェクションポイントを解決できるリソースアノテーションを指定できます。インテグレーターは、対応する io.quarkus.arc.ResourceReferenceProvider サービスプロバイダー実装も提供する必要があります。
ResourceAnnotationBuildItem Example@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>containing all the removed beans; see Removing unused beans for more information - 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
-
すべてのビルドメタデータにアクセスできます