CDI Integration Guide
Arc,Quarkus 中的 CDI 容器,是在构建时自举的。为了与容器集成,可以使用 CDI Build Compatible Extensions,以及 Quarkus 特定的扩展 API。CDI 可移植扩展不能被支持,并且不支持。本指南重点关注 Quarkus 特定的扩展 API。 容器在多个阶段自举。从高级别的角度来看,这些阶段如下:
-
Initialization
-
Bean discovery
-
Registration of synthetic components
-
Validation
在 initialization 阶段,预备工作正在进行,并且注册了自定义上下文。Bean discovery 然后是容器分析所有应用程序类、识别 bean 并根据提供的元数据将它们全部连接在一起的过程。随后,扩展可以注册 synthetic components。这些组件的属性完全由扩展控制,即,不是从现有类派生的。最后,是 deployment is validated。例如,容器验证应用程序中的每个注入点,并且如果没有满足给定所需类型和限定符的 bean,则构建失败。
通过启用附加日志记录,你可以看到有关 bootstrap 更多信息。只需使用 |
Quarkus 构建步骤可以生成和使用各种构建项,并连接到每个阶段。在以下部分中,我们将描述所有相关的构建项和常见场景。
- Metadata Sources
- Use Case - My Class Is Not Recognized as a Bean
- Use Case - My Annotation Is Not Recognized as a Qualifier or an Interceptor Binding
- Use Case - I Need To Transform Annotation Metadata
- Use Case - Inspect Beans, Observers and Injection Points
- Use Case - The Need for Synthetic Beans
- Use Case - Synthetic Observers
- Use Case - I Have a Generated Bean Class
- Use Case - I Need to Validate the Deployment
- Use Case - Register a Custom CDI Context
- Use Case - Additional Interceptor Bindings
- Use Case - Additional Qualifiers
- Use Case - Additional Stereotypes
- Use Case - Injection Point Transformation
- Use Case - Resource Annotations and Injection
- Available Build Time Metadata
Metadata Sources
类和注释是 bean 级元数据的来源。初始元数据从 bean archive index 中读取,这是一个不可变的 Jandex index,它在 bean discovery 期间由不同的来源构建。然而,扩展可以在 bootstrap 的某些阶段添加、移除或转换元数据。此外,扩展还可以注册 synthetic components。在 Quarkus 中集成 CDI 组件时,意识到这一点非常重要。
通过这种方式,扩展可以将原本会被忽略的类转换成 bean,反之亦然。例如,声明 @Scheduled
方法的类始终注册为 bean,即使它未使用定义 bean 的注释进行注释,并且通常会被忽略。
Use Case - My Class Is Not Recognized as a Bean
UnsatisfiedResolutionException
表示 typesafe resolution 期间有问题。有时即使类路径上存在一个似乎有资格注入的类,某个注入点也无法得到满足。一个类无法被识别的因素有很多,修复方法也有很多。在第一步中,我们应该识别 reason。
Reason 1: Class Is Not discovered
Quarkus 有 simplified discovery。有可能这个类不属于应用程序索引。例如,Quarkus 扩展的 runtime module 中的类不会自动编入索引。
Solution:使用 AdditionalBeanBuildItem
。此构建项可用于指定在发现期间要分析的一个或多个附加类。附加 bean 类将透明地添加到容器处理的应用程序索引中。
如 cdi-reference.adoc#enable_build_profile 和 cdi-reference.adoc#enable_build_properties 中所述,无法通过 @IfBuildProfile
、@UnlessBuildProfile
、@IfBuildProperty
和 @UnlessBuildProperty
注释有条件地启用/禁用附加 bean。扩展应检查配置或当前配置文件,并且仅在真正需要时才会生成 AdditionalBeanBuildItem
。
AdditionalBeanBuildItem
Example@BuildStep
AdditionalBeanBuildItem additionalBeans() {
return new AdditionalBeanBuildItem(SmallRyeHealthReporter.class, HealthServlet.class); 1
}
1 | AdditionalBeanBuildItem.Builder 可用于更复杂的使用案例。 |
默认情况下,通过 AdditionalBeanBuildItem
添加的 bean 类会被 removable。如果容器认为它们 unused,它们就会被忽略。但是,你可以使用 AdditionalBeanBuildItem.Builder.setUnremovable()
方法指示容器永远不要删除通过此构建项注册的 bean 类。有关更多详细信息,请参见 Removing Unused Beans 和 Reason 3: Class Was Discovered and Has a Bean Defining Annotation but Was Removed。
也可以通过 AdditionalBeanBuildItem.Builder#setDefaultScope()
设置默认范围。仅当 bean 类上未声明范围时才使用默认范围。
如果没有指定默认范围,则使用 |
Reason 2: Class Is Discovered but Has No Bean Defining Annotation
在 Quarkus 中,应用程序由具有 bean discovery mode annotated
的单个 bean 存档表示。因此,忽略了没有 bean defining annotation 的 bean 类。bean 定义注释在类级别上声明,包括范围、构造型和 @Interceptor
。
Solution 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 添加为默认范围。自动跳过已经用范围注释的类。 |
Solution 2:如果你需要处理带有特定注释的类,那么可以通过 BeanDefiningAnnotationBuildItem
扩展 bean 定义注释集。
BeanDefiningAnnotationBuildItem
Example@BuildStep
BeanDefiningAnnotationBuildItem additionalBeanDefiningAnnotation() {
return new BeanDefiningAnnotationBuildItem(Annotations.GRAPHQL_API); 1
}
1 | 将 org.eclipse.microprofile.graphql.GraphQLApi 添加到 bean 定义注释集。 |
默认情况下,通过 BeanDefiningAnnotationBuildItem
添加的 bean 类会被 not removable,即,即使结果 bean 被认为未使用,也不得删除。但是,你可以更改默认行为。有关更多详细信息,请参见 Removing Unused Beans 和 Reason 3: Class Was Discovered and Has a Bean Defining Annotation but Was Removed。
还可以指定默认范围。默认范围仅在没有在 bean 类上声明范围时使用。
如果没有指定默认范围,则使用 |
Reason 3: Class Was Discovered and Has a Bean Defining Annotation but Was Removed
容器默认尝试在生成期间执行 remove all unused beans。这一优化有助于 framework-level dead code elimination。在少数特殊情况下,无法正确标识未使用的 bean。特别是,Quarkus 尚不能检测到 `CDI.current()`静态方法的使用。扩展可以通过生成 `UnremovableBeanBuildItem`来消除可能出现的误报。
UnremovableBeanBuildItem
Example@BuildStep
UnremovableBeanBuildItem unremovableBeans() {
return UnremovableBeanBuildItem.targetWithAnnotation(STARTUP_NAME); 1
}
1 | 让所有使用 `@Startup`注释的类均无法移除。 |
Use Case - My Annotation Is Not Recognized as a Qualifier or an Interceptor Binding
很有可能是注释类不属于应用程序索引。例如,来自 Quarkus 扩展的 _runtime module_中的类不会自动编入索引。
Solution:根据 Reason 1: Class Is Not discovered中所述,使用 AdditionalBeanBuildItem
。
Use Case - I Need To Transform Annotation Metadata
在某些情况下,能够修改注释元数据非常有用。Quarkus 提供了 jakarta.enterprise.inject.spi.ProcessAnnotatedType
和 jakarta.enterprise.inject.build.compatible.spi.Enhancement
的强大替代方法。借助 AnnotationsTransformerBuildItem
,可以覆盖 bean 类上存在的注释。
请记住,注释转换器必须在 _before_开始 bean 发现后生成。 |
例如,您可能希望向特定 bean 类添加拦截器绑定。可以使用方便的生成器 API 来创建转换实例:
@BuildStep
AnnotationsTransformerBuildItem transform() {
return new AnnotationsTransformerBuildItem(AnnotationTransformation.forClasses() (1)
.whenClass(DotName.createSimple("org.acme.Bar")) (2)
.transform(t -> t.add(MyInterceptorBinding.class))); (3)
}
1 | 该转换器仅适用于类。 |
2 | 仅当该类为 `org.acme.Bar`时才应用该转换。 |
3 | Add the @MyInterceptorBinding annotation. |
上面的示例可以使用匿名类重写:
AnnotationsTransformerBuildItem
Example@BuildStep
AnnotationsTransformerBuildItem transform() {
return new AnnotationsTransformerBuildItem(new AnnotationTransformation() {
public boolean supports(AnnotationTarget.Kind kind) {
return kind == AnnotationTarget.Kind.CLASS; (1)
}
public void apply(TransformationContext context) {
if (context.declaration().asClass().name().toString().equals("org.acme.Bar")) {
context.add(MyInterceptorBinding.class); (2)
}
}
});
}
1 | 该转换器仅适用于类。 |
2 | 如果类名等于 org.acme.Bar ,则添加 @MyInterceptorBinding 。 |
仍然支持 ArC 中旧的 `AnnotationsTransformer`API,但更建议使用 Jandex 中新的 `AnnotationTransformation`API。 |
生成步骤可以通过 `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()`将返回一组可能已转换的注释。 |
还有其他一些专门用于转换的生成项目: Use Case - Additional Interceptor Bindings和 Use Case - Injection Point Transformation。 |
Use Case - Inspect Beans, Observers and Injection Points
Solution 1: BeanDiscoveryFinishedBuildItem
`BeanDiscoveryFinishedBuildItem`的使用者可以轻松检查应用程序中注册的所有基于类的 bean、观察者和注入点。但是,虚假 bean 和观察者是 _not included_的,因为此生成项目是在 _before_注册虚假组件后生成的。
此外,还可以使用 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。 |
Solution 2: SynthesisFinishedBuildItem
SynthesisFinishedBuildItem
的使用方可以轻松地检查应用程序中注册的所有 Bean、观察者和注入点。包含合成 Bean 和观察者,这是因为已注册合成组件时生成了此生成项目 after。
另外,可以利用 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。 |
Use Case - The Need for Synthetic Beans
有时候,注册 synthetic bean.Bean 是实用的。合成 bean 的属性不是来自 Java 类、方法或字段。而是,所有属性都由扩展定义。在常规 CDI 中,可以使用 AfterBeanDiscovery.addBean()
和 SyntheticComponents.addBean()
方法实现这一点。
Solution:如果你需要注册一个合成 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 配置器的输出被记录为字节码。因此,在运行时创建合成 bean 实例的方式存在一些限制。你可以:
-
通过
ExtendedBeanConfigurator.creator(Consumer<MethodCreator>)
直接生成Contextual#create(CreationalContext<T>)
方法的字节码。 -
通过
ExtendedBeanConfigurator#creator(Class<? extends BeanCreator<U>>)
传递一个io.quarkus.arc.BeanCreator
的子类,并可以通过ExtendedBeanConfigurator#param()
和ExtendedBeanConfigurator#addInjectionPoint()
指定一些构建时参数还有合成注入点。 -
通过从一个
@Recorder
method 返回的代理生成运行时实例,并通过ExtendedBeanConfigurator#runtimeValue(RuntimeValue<?>)
、ExtendedBeanConfigurator#runtimeProxy(Object)
、ExtendedBeanConfigurator#supplier(Supplier<?>)
或者ExtendedBeanConfigurator#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 | 默认情况下,将在 STATIC_INIT 期间初始化合成 Bean。 |
2 | Bean 实例由记录器方法返回的值提供。 |
可以在 RUNTIME_INIT
期间标记一个合成 Bean 以便被初始化。更多有关 STATIC_INIT
和 RUNTIME_INIT
之间差异的信息,请参见 Three Phases of Bootstrap and Quarkus Philosophy。
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 期间被初始化。 |
在 RUNTIME_INIT
期间初始化的合成 Bean 在 STATIC_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 初始化后执行。 |
还可以使用 |
Synthetic Injection Points
合成 bean 可通过 ExtendedBeanConfigurator#addInjectionPoint()
方法注册合成注入点。此注入点在构建时进行验证,并在 detecting unused beans 时考虑。注入的引用可通过 SyntheticCreationalContext#getInjectedReference()
方法在运行时访问。
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 的构造函数。 |
Use Case - Synthetic Observers
与 synthetic beans 类似,合成观察器方法的属性不是从 Java 方法派生的。相反,所有属性均由扩展定义。
Solution:如果需要注册合成观察器,请使用 ObserverRegistrationPhaseBuildItem
。
使用 ObserverRegistrationPhaseBuildItem
的构建步骤始终应该生成 ObserverConfiguratorBuildItem
,或者至少为此构建项目注入 BuildProducer
,否则它可能会被忽略或在错误的时间处理(例如,在正确的 CDI 引导阶段之后)。
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
的输出记录为字节码。因此,在运行时如何调用合成观察器存在一些限制。目前,你必须直接生成方法体的字节码。
Use Case - I Have a Generated Bean Class
没问题。你可以手动生成 bean 类的字节码,然后你需要做的就是生成 GeneratedBeanBuildItem
,而不是 GeneratedClassBuildItem
。
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 { } 。 |
Use Case - I Need to Validate the Deployment
有时扩展需要检查 bean、观察器和注入点,然后执行其他验证,并在出现问题时使构建失败。
Solution:如果扩展需要验证部署,应使用 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());
}
}
你可以从 |
Use Case - Register a Custom CDI Context
有时扩展需要扩展内置 CDI 上下文的集合。
Solution: 如果需要注册自定义上下文,请使用 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 上下文的扩展也应生成 CustomScopeBuildItem
,以便将自定义作用域注释名称贡献到 bean 定义注释的集合。
CustomScopeBuildItem
示例
@BuildStep
CustomScopeBuildItem customScope() {
return new CustomScopeBuildItem(DotName.createSimple(TransactionScoped.class.getName()));
}
Use Case - Additional Interceptor Bindings
在罕见的情况下,可能很方便以编程方式注册未通过 @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));
}
});
}
Use Case - Additional Qualifiers
有时可能需要注册未通过 @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());
}
});
}
Use Case - Additional Stereotypes
有时将未通过 @jakarta.enterprise.inject.Stereotype
注释为 CDI 刻板模式的现有注释进行注册很有用。这类似于 CDI 通过 BeforeBeanDiscovery#addStereotype()
实现的效果。我们要使用 StereotypeRegistrarBuildItem
来完成此操作。
StereotypeRegistrarBuildItem
Example@BuildStep
StereotypeRegistrarBuildItem addStereotypes() {
return new StereotypeRegistrarBuildItem(new StereotypeRegistrar() {
@Override
public Set<DotName> getAdditionalStereotypes() {
return Collections.singleton(DotName.createSimple(NotAStereotype.class.getName()));
}
});
}
如果新注册的刻板模式注释没有适当的元注释,例如作用域或拦截器绑定,请使用 annotation transformation 来添加它们。
Use Case - Injection Point Transformation
每隔一段时间就有必要能够以编程方式更改注入点的限定符。你可以使用 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();
}
}
});
}
理论上,可以使用 an |
Use Case - Resource Annotations and Injection
ResourceAnnotationBuildItem
可用于指定资源注释,这样便于解析非 CDI 注入点,例如 Jakarta EE 资源。集成商还必须提供相应的 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())));
}
Available Build Time Metadata
使用 BuildExtension.BuildContext
运行的上述任何扩展都可以利用构建期间生成的一些构建时间元数据。位于 io.quarkus.arc.processor.BuildExtension.Key
中的内置键为:
- ANNOTATION_STORE
-
Contains an
AnnotationStore
that keeps information about allAnnotationTarget
annotations after application of annotation transformers - INJECTION_POINTS
-
Collection<InjectionPointInfo>
containing all injection points - BEANS
-
Collection<BeanInfo>
containing all beans - REMOVED_BEANS
-
Collection<BeanInfo>
containing all the removed beans; see Removing unused beans for more information - OBSERVERS
-
Collection<ObserverInfo>
containing all observers - SCOPES
-
Collection<ScopeInfo>
containing all scopes, including custom ones - QUALIFIERS
-
Map<DotName, ClassInfo>
containing all qualifiers - INTERCEPTOR_BINDINGS
-
Map<DotName, ClassInfo>
containing all interceptor bindings - STEREOTYPES
-
Map<DotName, StereotypeInfo>
containing all stereotypes
要掌握这些键,只需针对给定的键查询扩展上下文对象。请注意,这些元数据在构建过程中作为可用信息,这意味着扩展只能利用在调用扩展之前构建的元数据。如果你的扩展尝试检索尚未生成的元数据,则会返回 null
。下面总结了哪些扩展可以访问哪些元数据:
- AnnotationsTransformer
-
Shouldn’t rely on any metadata as it could be used at any time in any phase of the bootstrap
- ContextRegistrar
-
Has access to
ANNOTATION_STORE
,QUALIFIERS
,INTERCEPTOR_BINDINGS
,STEREOTYPES
- InjectionPointsTransformer
-
Has access to
ANNOTATION_STORE
,QUALIFIERS
,INTERCEPTOR_BINDINGS
,STEREOTYPES
- ObserverTransformer
-
Has access to
ANNOTATION_STORE
,QUALIFIERS
,INTERCEPTOR_BINDINGS
,STEREOTYPES
- BeanRegistrar
-
Has access to
ANNOTATION_STORE
,QUALIFIERS
,INTERCEPTOR_BINDINGS
,STEREOTYPES
,BEANS
(class-based beans only),OBSERVERS
(class-based observers only),INJECTION_POINTS
- ObserverRegistrar
-
Has access to
ANNOTATION_STORE
,QUALIFIERS
,INTERCEPTOR_BINDINGS
,STEREOTYPES
,BEANS
,OBSERVERS
(class-based observers only),INJECTION_POINTS
- BeanDeploymentValidator
-
Has access to all build metadata