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

Guia de Integração CDI

ArC, the CDI container in Quarkus, is bootstrapped at build time. To integrate with the container, CDI Build Compatible Extensions can be used, as well as a Quarkus-specific extension API. CDI Portable Extensions are not and cannot be supported. This guide focuses on the Quarkus-specific extensions API.

O contêiner é inicializado em várias fases. De uma perspectiva de alto nível, essas fases são as seguintes:

  1. Inicialização

  2. Descoberta do Bean

  3. Registo de componentes sintéticos

  4. Validação

In the initialization phase, the preparatory work is being carried out, and custom contexts are registered. Bean discovery is then the process where the container analyzes all application classes, identifies beans and wires them all together based on the provided metadata. Subsequently, the extensions can register synthetic components. Attributes of these components are fully controlled by the extensions, i.e. are not derived from an existing class. Finally, the deployment is validated. For example, the container validates every injection point in the application and fails the build if there is no bean that satisfies the given required type and qualifiers.

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 build steps can produce and consume various build items and hook into each phase. In the following sections, we will describe all the relevant build items and common scenarios.

1. Fontes de metadados

Classes and annotations are the primary source of bean-level metadata. The initial metadata are read from the bean archive index, an immutable Jandex index which is built from various sources during bean discovery. However, extensions can add, remove or transform the metadata at certain stages of the bootstrap. Moreover, extensions can also register synthetic components. This is an important aspect to realize when integrating CDI components in Quarkus.

Dessa forma, as extensões podem transformar classes, que de outra forma seriam ignoradas, em beans e vice-versa. Por exemplo, uma classe que declara um método @Scheduled é sempre registrada como um bean, mesmo que não tenha sido anotada com uma anotação de definição de bean e, normalmente, seria ignorada.

2. Caso de uso - Minha classe não é reconhecida como um Bean

An UnsatisfiedResolutionException indicates a problem during typesafe resolution. Sometimes an injection point cannot be satisfied even if there is a class on the classpath that appears to be eligible for injection. There are several reasons why a class is not recognized and also several ways to fix it. In the first step, we should identify the reason.

2.1. Razão 1: Classe Não Está Descoberta

Quarkus has a simplified discovery. It might happen that the class is not part of the application index. For example, classes from the runtime module of a Quarkus extension are not indexed automatically.

Solução : Use o AdditionalBeanBuildItem . Esse item de construção pode ser usado para especificar uma ou mais classes adicionais a serem analisadas durante a descoberta. As classes de bean adicionais são adicionadas de forma transparente ao índice da aplicação processado pelo contêiner.

It is not possible to conditionally enable/disable additional beans via the @IfBuildProfile, @UnlessBuildProfile, @IfBuildProperty and @UnlessBuildProperty annotations as described in cdi-reference and cdi-reference. Extensions should inspect the configuration or the current profile and only produce an AdditionalBeanBuildItem if really needed.
Exemplo de AdditionalBeanBuildItem
@BuildStep
AdditionalBeanBuildItem additionalBeans() {
     return new AdditionalBeanBuildItem(SmallRyeHealthReporter.class, HealthServlet.class); (1)
}
1 AdditionalBeanBuildItem.Builder pode ser utilizado para casos de uso mais complexos.

Bean classes added via AdditionalBeanBuildItem are removable by default. If the container considers them unused, they are just ignored. However, you can use AdditionalBeanBuildItem.Builder.setUnremovable() method to instruct the container to never remove bean classes registered via this build item. See also Removing Unused Beans and Motivo 3: A Classe Foi Descoberta e Tem uma Anotação que Define o Bean, mas Foi Removida for more details.

It is also possible to set the default scope via AdditionalBeanBuildItem.Builder#setDefaultScope(). The default scope is only used if there is no scope declared on the bean class.

Se não for especificado um escopo padrão, é utilizado o pseudo escopo @Dependent.

2.2. Motivo 2: A Classe É Descoberta mas Não Tem uma Anotação de Definição de 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.

Solução 1: Utilizar o AutoAddScopeBuildItem. Este item de construção pode ser utilizado para adicionar um escopo a uma classe que satisfaça determinadas condições.

Exemplo de AutoAddScopeBuildItem
@BuildStep
AutoAddScopeBuildItem autoAddScope() {
   return AutoAddScopeBuildItem.builder().containsAnnotations(SCHEDULED_NAME, SCHEDULES_NAME) (1)
      .defaultScope(BuiltinScope.SINGLETON) (2)
      .build();
}
1 Encontrar todas as classes anotadas com @Scheduled.
2 Adicione @Singleton como escopo padrão. As classes já anotadas com um escopo são ignoradas automaticamente.

Solution 2: If you need to process classes annotated with a specific annotation, then it’s possible to extend the set of bean defining annotations via the BeanDefiningAnnotationBuildItem.

Exemplo de BeanDefiningAnnotationBuildItem
@BuildStep
BeanDefiningAnnotationBuildItem additionalBeanDefiningAnnotation() {
   return new BeanDefiningAnnotationBuildItem(Annotations.GRAPHQL_API); (1)
}
1 Adicionar org.eclipse.microprofile.graphql.GraphQLApi ao conjunto de anotações de definição de bean.

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 behaviour. See also Removing Unused Beans and Motivo 3: A Classe Foi Descoberta e Tem uma Anotação que Define o Bean, mas Foi Removida for more details.

Também é possível especificar o escopo padrão. O escopo padrão é usado somente se não houver escopo declarado na classe do bean.

Se não for especificado um escopo padrão, é utilizado o pseudo escopo @Dependent.

2.3. Motivo 3: A Classe Foi Descoberta e Tem uma Anotação que Define o Bean, mas Foi Removida

The container attempts to remove all unused beans during the build by default. This optimization allows for framework-level dead code elimination. In a few special cases, it’s not possible to correctly identify an unused bean. In particular, Quarkus is not able to detect the usage of the CDI.current() static method yet. Extensions can eliminate possible false positives by producing an UnremovableBeanBuildItem.

Exemplo de UnremovableBeanBuildItem
@BuildStep
UnremovableBeanBuildItem unremovableBeans() {
   return UnremovableBeanBuildItem.targetWithAnnotation(STARTUP_NAME); (1)
}
1 Tornar todas as classes anotadas com @Startup não removíveis.

3. Caso de uso - Minha Anotação não É Reconhecida Como Um Qualificador ou Uma Ligação de Interceptador

É provável que a classe de anotação não faça parte do índice da aplicação. Por exemplo, as classes do módulo de tempo de execução de uma extensão do Quarkus não são indexadas automaticamente.

Solution: Use the AdditionalBeanBuildItem as described in Razão 1: Classe Não Está Descoberta.

4. Caso de Utilização - Preciso Transformar Metadados de Anotações

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.

Tenha em mente que os transformadores de anotação devem ser produzidos antes do início da descoberta de bean.

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

Exemplo de Builder
@BuildStep
AnnotationsTransformerBuildItem transform() {
    return new AnnotationsTransformerBuildItem(AnnotationTransformation.forClasses() (1)
        .whenClass(DotName.createSimple("org.acme.Bar")) (2)
        .transform(t -> t.add(MyInterceptorBinding.class))); (3)
}
1 O transformador só é aplicado às classes.
2 Only apply the transformation if the class is org.acme.Bar.
3 Adicionar a anotação @MyInterceptorBinding.

O exemplo acima pode ser reescrito com uma classe anônima:

Exemplo de AnnotationsTransformerBuildItem
@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 O transformador só é aplicado às classes.
2 If the class name equals to org.acme.Bar then add @MyInterceptorBinding.
The previous AnnotationsTransformer API from ArC is still supported, but the new AnnotationTransformation API from Jandex is preferred.

As etapas de construção podem consultar as anotações transformadas para um determinado alvo de anotação através do TransformedAnnotationsBuildItem.

Exemplo de TransformedAnnotationsBuildItem
@BuildStep
void queryAnnotations(TransformedAnnotationsBuildItem transformedAnnotations,
        BuildProducer<MyBuildItem> myBuildItem) {
    ClassInfo myClazz = ...;
    if (transformedAnnotations.getAnnotations(myClazz).isEmpty()) { (1)
        myBuildItem.produce(new MyBuildItem());
    }
}
1 TransformedAnnotationsBuildItem.getAnnotations() devolverá um conjunto de anotações possivelmente transformadas.

4.1. Como Ativar o Log de Trace para Transformadores de Anotações

Pode definir o nível de TRACE para a categoria io.quarkus.arc.processor e tentar analisar a saída do log posteriormente.

Exemplo de application.properties
quarkus.log.category."io.quarkus.arc.processor".min-level=TRACE (1)
quarkus.log.category."io.quarkus.arc.processor".level=TRACE
1 Também é necessário ajustar o nível mínimo de log para a categoria relevante.

5. Caso de Uso - Inspecionar Beans, Observadores e Pontos de Injeção

5.1. Solução 1: BeanDiscoveryFinishedBuildItem

Os consumidores do BeanDiscoveryFinishedBuildItem podem inspecionar facilmente todos os beans, observadores e pontos de injeção baseados em classe registrados na aplicação. No entanto, os beans e observadores sintéticos não são incluídos porque esse item de construção é produzido antes que os componentes sintéticos sejam registrados.

Além disso, o resolvedor de beans retornado por BeanDiscoveryFinishedBuildItem#getBeanResolver() pode ser utilizado para aplicar as regras de resolução de tipagem segura, por exemplo, para descobrir se existe um bean que satisfaça uma determinada combinação de tipo e qualificadores necessários.

Exemplo de BeanDiscoveryFinishedBuildItem
@BuildStep
void doSomethingWithNamedBeans(BeanDiscoveryFinishedBuildItem beanDiscovery, BuildProducer<NamedBeansBuildItem> namedBeans) {
   List<BeanInfo> namedBeans = beanDiscovery.beanStream().withName().collect(toList())); (1)
   namedBeans.produce(new NamedBeansBuildItem(namedBeans));
}
1 A lista resultante não conterá beans sintéticos @Named.

5.2. Solução 2: SynthesisFinishedBuildItem

Os consumidores de SynthesisFinishedBuildItem podem inspecionar facilmente todos os beans, observadores e pontos de injeção registrados na aplicação. Os beans e observadores sintéticos estão incluídos porque este item de construção é produzido depois dos componentes sintéticos serem registrados.

Além disso, o resolvedor de beans retornado por SynthesisFinishedBuildItem#getBeanResolver() pode ser utilizado para aplicar as regras de resolução de tipagem segura, por exemplo, para descobrir se existe um bean que satisfaça uma determinada combinação de tipo e qualificadores necessários.

Exemplo de SynthesisFinishedBuildItem
@BuildStep
void doSomethingWithNamedBeans(SynthesisFinishedBuildItem synthesisFinished, BuildProducer<NamedBeansBuildItem> namedBeans) {
   List<BeanInfo> namedBeans = synthesisFinished.beanStream().withName().collect(toList())); (1)
   namedBeans.produce(new NamedBeansBuildItem(namedBeans));
}
1 A lista resultante conterá beans sintéticos @Named.

6. Caso de Uso - A Necessidade de Beans Sintéticos

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.

Solução: Se for necessário registrar um bean sintético, utilize o SyntheticBeanBuildItem.

Exemplo 1 de SyntheticBeanBuildItem
@BuildStep
SyntheticBeanBuildItem syntheticBean() {
   return SyntheticBeanBuildItem.configure(String.class)
             .qualifiers(AnnotationInstance.builder(MyQualifier.class).build())
             .creator(mc -> mc.returnValue(mc.load("foo"))) (1)
             .done();
}
1 Gerar o bytecode da implementação jakarta.enterprise.context.spi.Contextual#create(CreationalContext<T>).

A saída de um configurador de bean é gravada como bytecode. Portanto, há algumas limitações na forma como uma instância de bean sintético é criada em tempo de execução. Você pode:

  1. Gerar o bytecode do método Contextual#create(CreationalContext<T>) diretamente através do ExtendedBeanConfigurator.creator(Consumer<MethodCreator>).

  2. Passar uma subclasse de io.quarkus.arc.BeanCreator através do ExtendedBeanConfigurator#creator(Class<? extends BeanCreator<U>>), e possivelmente especificar alguns parâmetros de tempo de construção através do ExtendedBeanConfigurator#param() e pontos de injeção sintéticos através do 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<?>, <?>).

Exemplo 2 de SyntheticBeanBuildItem
@BuildStep
@Record(STATIC_INIT) (1)
SyntheticBeanBuildItem syntheticBean(TestRecorder recorder) {
   return SyntheticBeanBuildItem.configure(Foo.class).scope(Singleton.class)
                .runtimeValue(recorder.createFoo()) (2)
                .done();
}
1 Por padrão, um bean sintético é inicializado durante STATIC_INIT.
2 A instância do bean é fornecida por um valor retornado por um método de gravação.

It is also possible to create a generic synthetic bean Foo<Bar>.

SyntheticBeanBuildItem Example 3
@BuildStep
@Record(STATIC_INIT)
SyntheticBeanBuildItem syntheticBean(TestRecorder recorder) {
   return SyntheticBeanBuildItem.configure(Foo.class)
                .types(ParameterizedType.create(Foo.class, ClassType.create(Bar.class)))) (1)
                .scope(Singleton.class)
                .runtimeValue(recorder.createFooBar())
                .done();
}
1 types() or addType() must be used to specify the generic type.

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.

Exemplo de 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 O gravador deve ser executado na fase ExecutionTime.RUNTIME_INIT.
2 A instância do bean é inicializada durante RUNTIME_INIT.

Os beans sintéticos inicializados durante RUNTIME_INIT não devem ser acessados durante STATIC_INIT. As etapas de construção de RUNTIME_INIT que acessam um bean sintético inicializado em tempo de execução devem consumir o SyntheticBeansRuntimeInitBuildItem:

@BuildStep
@Record(RUNTIME_INIT)
@Consume(SyntheticBeansRuntimeInitBuildItem.class) (1)
void accessFoo(TestRecorder recorder) {
   recorder.foo(); (2)
}
1 Este passo de construção deve ser executado após a conclusão de syntheticBean().
2 Este método de gravação resulta numa invocação da instância do bean Foo e, por isso, temos de nos certificar de que a etapa de construção é executada depois de todos os beans sintéticos serem inicializados.
Também é possível utilizar o BeanRegistrationPhaseBuildItem para registrar um bean sintético. No entanto, recomendamos aos autores de extensões que utilizem SyntheticBeanBuildItem, que é mais idiomático para o Quarkus.

6.1. Pontos de Injeção Sintéticos

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.

Ponto de Injeção Sintético - Exemplo de Etapa de Construção
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 A instância do bean é inicializada durante RUNTIME_INIT.
2 Foi adicionado um ponto de injeção sintético com o tipo obrigatório Bar; este é equivalente a @Inject Bar.
3 A instância do bean é criada com uma função retornada de um método de gravação.
Ponto de Injeção Sintético - Exemplo de Gravador
@Recorder
public class TestRecorder {

   public Function<SyntheticCreationalContext<Foo>, Foo> createFoo() {
     return (context) -> {
        return new Foo(context.getInjectedReference(Bar.class)); (1)
     };
   }
}
1 Passa uma referência contextual de Bar ao construtor de Foo.

6.2. Inactive Synthetic Beans

In the case when one needs to register multiple synthetic beans at build time but only wants a subset of them active at runtime, it is useful to be able to mark a synthetic bean as inactive. This is done by configuring a "check active" procedure, which should be a Supplier<ActiveResult> obtained from a recorder:

Inactive Synthetic Bean - Build Step Example
@BuildStep
@Record(RUNTIME_INIT)
SyntheticBeanBuildItem syntheticBean(TestRecorder recorder) {
    return SyntheticBeanBuildItem.configure(Foo.class)
            .scope(Singleton.class)
            .startup() (1)
            .checkActive(recorder.isFooActive()) (2)
            .createWith(recorder.createFoo())
            .done();
}
1 A bean that might be inactive is typically initialized eagerly, to make sure that an error is thrown at application startup. If the bean is in fact inactive, but is not injected into an always-active bean, eager initialization is skipped and no error is thrown.
2 Configures the "check active" procedure.
Inactive Synthetic Bean - Recorder Example
@Recorder
public class TestRecorder {
    public Supplier<ActiveResult> isFooActive() {
        return () -> {
            if (... should not be active ...) { (1)
                return ActiveResult.inactive("explanation"); (2)
            }
            return ActiveResult.active();
        };
    }

    public Function<SyntheticCreationalContext<Foo>, Foo> createFoo() {
        return (context) -> {
            return new Foo();
        };
    }
}
1 The condition when the synthetic bean should be inactive.
2 Proper explanation of why the bean is inactive. Another inactive ActiveResult may also be provided as a cause, if this bean’s inactivity stems from another bean’s inactivity.

If an inactive bean is injected somewhere, or is dynamically looked up, an InactiveBeanException is thrown. The error message contains the reason (from the ActiveResult), the cause chain (also from the ActiveResult), and possibly also a list of all injection points that resolve to this bean.

If you want to handle the inactive case gracefully, you should always inject possibly inactive beans using Instance<>. You also need to check before obtaining the actual instance:

import io.quarkus.arc.InjectableInstance;

@Inject
InjectableInstance<Foo> foo;

if (foo.getHandle().getBean().isActive()) {
    Foo foo = foo.get();
    ...
}

If you want to consume only active beans, you can inject an InjectableInstance<> and call getActive() to get the single instance or listActive() to get all instances:

import io.quarkus.arc.InjectableInstance;

@Inject
@Any
InjectableInstance<Foo> foos;

for (Foo foo : foos.listActive())
    ...
}

7. Caso de Uso - Observadores Sintéticos

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.

Solução: Se for necessário registrar um observador sintético, utilize o ObserverRegistrationPhaseBuildItem.

Uma etapa de construção que consome o ObserverRegistrationPhaseBuildItem deve sempre produzir um ObserverConfiguratorBuildItem ou, pelo menos, injetar um BuildProducer para este item de construção, caso contrário, pode ser ignorado ou processado no momento errado (por exemplo, após a fase de bootstrap correta do CDI).
Exemplo de 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());
}

The output of an ObserverConfigurator is recorded as bytecode. Therefore, there are some limitations in how a synthetic observer is invoked at runtime. Currently, you must generate the bytecode of the method body directly.

8. Caso de Uso - Tenho uma Classe de Bean Gerada

No problem. You can generate the bytecode of a bean class manually, and then all you need to do is to produce a GeneratedBeanBuildItem instead of GeneratedClassBuildItem.

Exemplo de 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 facilita a produção de GeneratedBeanBuildItems a partir de constructos Gizmo.
2 A classe de bean resultante é algo como public class @Singleton MyBean { }.

9. Caso de Uso - Preciso Validar a Implantação

Por vezes, as extensões precisam de inspecionar os beans, os observadores e os pontos de injeção e, em seguida, efetuar validações adicionais e falhar a construção se algo estiver errado.

Solution: If an extension needs to validate the deployment, it should use the ValidationPhaseBuildItem.

Uma etapa de construção que consome o ValidationPhaseBuildItem deve sempre produzir um ValidationErrorBuildItem ou, pelo menos, injetar um BuildProducer para este item de construção, caso contrário, pode ser ignorado ou processado no momento errado (por exemplo, após a fase de bootstrap correta do CDI).
@BuildStep
void validate(ValidationPhaseBuildItem validationPhase,
            BuildProducer<MyBuildItem> myBuildItem,
            BuildProducer<ValidationErrorBuildItem> errors) {
   if (someCondition) {
     errors.produce(new ValidationErrorBuildItem(new IllegalStateException()));
     myBuildItem.produce(new MyBuildItem());
   }
}
Você pode filtrar facilmente todos os beans registrados através do prático BeanStream retornado pelo método ValidationPhaseBuildItem.getContext().beans().

10. Caso de Uso - Registrar um Contexto CDI Personalizado

Às vezes, as extensões precisam estender o conjunto de contextos CDI integrados.

Solução: Se for necessário registrar um contexto personalizado, utilize o ContextRegistrationPhaseBuildItem.

Uma etapa de construção que consome o ContextRegistrationPhaseBuildItem deve sempre produzir um ContextConfiguratorBuildItem ou, pelo menos, injetar um BuildProducer para este item de construção, caso contrário, pode ser ignorado ou processado no momento errado (por exemplo, após a fase de bootstrap correta do CDI).

Exemplo de ContextRegistrationPhaseBuildItem

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

Além disso, cada extensão que registra um contexto CDI personalizado através do ContextRegistrationPhaseBuildItem deve também produzir o CustomScopeBuildItem de modo a contribuir com o nome da anotação de escopo personalizado para o conjunto de anotações que definem o bean.

Exemplo de CustomScopeBuildItem

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

10.1. E Se Eu Precisar Conhecer Todos os Escopos Utilizados na Aplicação?

Solução: Você pode injetar o CustomScopeAnnotationsBuildItem numa etapa de construção e utilizar os métodos convenientes, como CustomScopeAnnotationsBuildItem.isScopeDeclaredOn().

11. Caso de Uso - Ligações de Interceptadores Adicionais

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.

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

12. Caso de Uso - Qualificadores Adicionais

Às vezes, pode ser útil registrar uma anotação existente que não esteja anotada com @jakarta.inject.Qualifier como um qualificador CDI. Isso é semelhante ao que a CDI faz por meio do BeforeBeanDiscovery#addQualifier() . Vamos usar o QualifierRegistrarBuildItem para fazer isso.

Exemplo de 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. Caso de Uso - Estereótipos Adicionais

Às vezes, é útil registrar uma anotação existente que não esteja anotada com @jakarta.enterprise.inject.Stereotype como um estereótipo CDI. Isso é semelhante ao que a CDI faz por meio do BeforeBeanDiscovery#addStereotype() . Vamos usar o StereotypeRegistrarBuildItem para fazer isso.

Exemplo de StereotypeRegistrarBuildItem
@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. Caso de Uso - Transformação de Pontos de Injeção

Every now and then, it is handy to be able to change the qualifiers of an injection point programmatically. You can do just that with InjectionPointTransformerBuildItem. The following sample shows how to apply a transformation to injection points with type Foo that contain a qualifier MyQualifier:

Exemplo de 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();
            }
        }
    });
}
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. Caso de Uso - Anotações e Injeção de Recursos

O ResourceAnnotationBuildItem pode ser usado para especificar anotações de recursos que possibilitam a resolução de pontos de injeção não CDI, como os recursos do Jakarta EE. Um integrador também deve fornecer uma implementação correspondente do provedor de serviços io.quarkus.arc.ResourceReferenceProvider.

Exemplo de 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. Metadados de Tempo de Construção Disponíveis

Qualquer uma das extensões acima que opere com BuildExtension.BuildContext pode aproveitar determinados metadados de tempo de construção gerados durante a construção. As chaves integradas, localizadas em io.quarkus.arc.processor.BuildExtension.Key são:

ANNOTATION_STORE

Contém um AnnotationStore que mantém informações sobre todas as anotações AnnotationTarget após a aplicação de transformadores de anotações

INJECTION_POINTS

Collection<InjectionPointInfo> contendo todos os pontos de injeção

BEANS

Collection<BeanInfo> contendo todos os beans

REMOVED_BEANS

Collection<BeanInfo> containing all the removed beans; see Removing unused beans for more information

OBSERVERS

Collection<ObserverInfo> contendo todos os observadores

SCOPES

Collection<ScopeInfo> contendo todos os escopos, incluindo os personalizados

QUALIFIERS

Map<DotName, ClassInfo> contendo todos os qualificadores

INTERCEPTOR_BINDINGS

Map<DotName, ClassInfo> contendo todas os vínculos de interceptadores

STEREOTYPES

Map<DotName, StereotypeInfo> contendo todos os estereótipos

To get hold of these, simply query the extension context object for given key. Note that these metadata are made available as build proceeds, which means that extensions can only leverage metadata that were built before the extensions are invoked. If your extension attempts to retrieve metadata that wasn’t yet produced, null will be returned. Here is a summary of which extensions can access which metadata:

AnnotationsTransformer

Não deve depender de quaisquer metadados, uma vez que pode ser utilizado em qualquer momento de qualquer fase do bootstrap

ContextRegistrar

Tem acesso a ANNOTATION_STORE, QUALIFIERS, INTERCEPTOR_BINDINGS, STEREOTYPES

InjectionPointsTransformer

Tem acesso a ANNOTATION_STORE, QUALIFIERS, INTERCEPTOR_BINDINGS, STEREOTYPES

ObserverTransformer

Tem acesso a ANNOTATION_STORE, QUALIFIERS, INTERCEPTOR_BINDINGS, STEREOTYPES

BeanRegistrar

Tem acesso a ANNOTATION_STORE, QUALIFIERS, INTERCEPTOR_BINDINGS, STEREOTYPES, BEANS (apenas beans baseados em classes), OBSERVERS (apenas observadores baseados em classes), INJECTION_POINTS

ObserverRegistrar

Tem acesso a ANNOTATION_STORE, QUALIFIERS, INTERCEPTOR_BINDINGS, STEREOTYPES, BEANS, OBSERVERS (apenas observadores baseados em classes), INJECTION_POINTS

BeanDeploymentValidator

Tem acesso a todos os metadados de construção

Conteúdo Relacionado