Quarkus - Contexts and Dependency Injection

Quarkus DI solution is based on the Contexts and Dependency Injection for Java 2.0 specification. However, it is not a full CDI implementation verified by the TCK. Only a subset of the CDI features is implemented - see also the list of supported features and the list of limitations.

Most of the existing CDI code should work just fine but there are some small differences which follow from the Quarkus architecture and goals.

1. Bean Discovery

Bean discovery in CDI is a complex process which involves legacy deployment structures and accessibility requirements of the underlying module architecture. Quarkus is using a simplified bean discovery. There is only one bean archive with annotated bean discovery mode and no visibility boundaries.

The bean archive is synthesized from:

  • the application,

  • application dependencies that contain a beans.xml descriptor or a generated Jandex index (META-INF/jandex.idx),

  • and Quarkus integration code.

Bean classes that don’t have a bean defining annotation are not discovered. This behavior is defined by CDI. But producer methods and fields and observer methods are discovered even if the declaring class is not annotated with a bean defining annotation (this behavior is different to what is defined in CDI). In fact, the declaring bean classes are considered annotated with @Dependent.

Quarkus extension may declare additional discovery rules. For example @Scheduled business methods are registered even if the declaring class is not annotated with a bean defining annotation.

2. Private Members

Quarkus is designed with Substrate VM in mind. One of the limitations is the usage of Reflection. Substrate VM does support reflective calls but for a price of a bigger native executable. Quarkus must use reflection fallback to access private members. That’s why Quarkus users are encouraged not to use private members in their beans. This involves injection fields, constructors and initializers, observer methods, producer methods and fields, disposers and interceptor methods.

You can use for example package-private modifiers:

public class CounterBean {

    CounterService counterService; (1)

    void onMessage(@Observes Event msg) { (2)
  1. A package-private injection field

  2. A package-private observer method

Or constructor injection:

public class CounterBean {

    private final CounterService service;

    CounterBean() { (1)

    CounterBean(CounterService service) { (2)
      this.service = service;
  1. Dummy constructor needed for CDI beans with a normal scope

  2. A package-private constructor injection

3. Supported Features

  • Programming model

    • Managed beans implemented by a Java class

      • @PostConstruct and @PreDestroy lifecycle callbacks

    • Producer methods and fields, disposers

    • Qualifiers

    • Alternatives

    • Stereotypes

  • Dependency injection and lookup

    • Field, constructor and initializer/setter injection

    • Type-safe resolution

    • Programmatic lookup via javax.enterprise.inject.Instance

    • Client proxies

    • Injection point metadata [1]

  • Scopes and contexts

    • @Dependent, @ApplicationScoped, @Singleton, @RequestScoped and @SessionScoped

    • Custom scopes and contexts

  • Interceptors

    • Business method interceptors: @AroundInvoke

    • Interceptors for lifecycle event callbacks: @PostConstruct, @PreDestroy, @AroundConstruct

  • Events and observers, including asynchronous events

4. Limitations

  • @ConversationScoped is not supported

  • Decorators are not supported

  • Portable Extensions are not supported

  • BeanManager - only the following methods are implemented: getBeans(), createCreationalContext(), getReference(), resolve(), getContext(), getEvent() and createInstance()

  • Specialization is not supported

  • beans.xml descriptor content is ignored

  • Passivation and passivating scopes are not supported

  • Transitive interceptor bindings and interceptor methods on superclasses are not implemented yet

5. Build Time Extension Points

5.1. Portable Extensions

Quarkus incorporates build-time optimizations in order to provide instant startup and low memory footprint. The downside of this approach is that CDI Portable Extensions cannot be supported. Nevertheless, most of the functionality can be achieved using Quarkus extensions.

5.2. Additional Bean Defining Annotations

As described in Bean Discovery bean classes that don’t have a bean defining annotation are not discovered. However, BeanDefiningAnnotationBuildItem can be used to extend the set of default bean defining annotations (@Dependent, @Singleton, @ApplicationScoped, @RequestScoped and @Stereotype annotations):

BeanDefiningAnnotationBuildItem additionalBeanDefiningAnnotation() {
    return new BeanDefiningAnnotationBuildItem(DotName.createSimple("javax.ws.rs.Path")));
Bean registrations that are result of a BeanDefiningAnnotationBuildItem are unremovable by default. See also Removing Unused Beans.

5.3. Resource Annotations

ResourceAnnotationBuildItem is used to specify resource annotations that make it possible to resolve non-CDI injection points, such as Java EE resources.

An integrator must also provide a corresponding io.quarkus.arc.ResourceReferenceProvider implementation.
void setupResourceInjection(BuildProducer<ResourceAnnotationBuildItem> resourceAnnotations, BuildProducer<GeneratedResourceBuildItem> resources) {
    resources.produce(new GeneratedResourceBuildItem("META-INF/services/io.quarkus.arc.ResourceReferenceProvider",
    resourceAnnotations.produce(new ResourceAnnotationBuildItem(DotName.createSimple(PersistenceContext.class.getName())));

5.4. Additional Beans

AdditionalBeanBuildItem is used to specify additional bean classes to be analyzed during discovery. Additional bean classes are transparently added to the application index processed by the container.

List<AdditionalBeanBuildItem> additionalBeans() {
     return Arrays.asList(
          new AdditionalBeanBuildItem(SmallRyeHealthReporter.class),
          new AdditionalBeanBuildItem(HealthServlet.class));
A bean registration that is a result of an AdditionalBeanBuildItem is removable by default. See also Removing Unused Beans.

5.5. Synthetic Beans

Sometimes it is very useful to register a synthetic bean, i.e. a bean that doesn’t need to have a corresponding java class. In CDI this could be achieved using AfterBeanDiscovery.addBean() methods. In Quarkus we produce a BeanRegistrarBuildItem and leverage the io.quarkus.arc.processor.BeanConfigurator API to build a synthetic bean definition.

BeanRegistrarBuildItem syntheticBean() {
     return new BeanRegistrarBuildItem(new BeanRegistrar() {

            public void register(RegistrationContext registrationContext) {
                 registrationContext.configure(String.class).types(String.class).qualifiers(new MyQualifierLiteral()).creator(mc -> mc.returnValue(mc.load("foo"))).done();
The output of a BeanConfigurator is recorded as bytecode. Therefore there are some limitations in how a synthetic bean instance is created. See also BeanConfigurator.creator() methods.

5.6. Annotation Transformations

A very common task is to override the annotations found on the bean classes. For example you might want to add an interceptor binding to a specific bean class. Here is how to do it - use the AnnotationsTransformerBuildItem:

AnnotationsTransformerBuildItem transform() {
    return new AnnotationsTransformerBuildItem(new AnnotationsTransformer() {

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

        public void transform(TransformationContext context) {
            if (contex.getTarget().asClass().name().toString().equals("com.foo.Bar")) {

5.7. Bean Deployment Validation

Once the bean deployment is ready an extension can perform additional validations and inspect the found beans, observers and injection points. Register a BeanDeploymentValidatorBuildItem:

BeanDeploymentValidatorBuildItem beanDeploymentValidator() {
    return new BeanDeploymentValidatorBuildItem(new BeanDeploymentValidator() {
         public void validate(ValidationContext validationContext) {
             for (InjectionPointInfo injectionPoint : validationContext.get(Key.INJECTION_POINTS)) {
                 System.out.println("Injection point: " + injectionPoint);
See also io.quarkus.arc.processor.BuildExtension.Key to discover the available metadata.

5.8. Custom Contexts

An extension can register a custom InjectableContext implementation by means of a ContextRegistrarBuildItem:

ContextRegistrarBuildItem customContext() {
    return new ContextRegistrarBuildItem(new ContextRegistrar() {
         public void register(RegistrationContext registrationContext) {

6. Removing Unused Beans

The container attempts to remove all unused beans during build by default. This optimization can be disabled: quarkus.arc.remove-unused-beans=false.

An unused bean:

  • is not a built-in bean or an interceptor,

  • is not eligible for injection to any injection point,

  • is not excluded by any extension,

  • does not have a name,

  • does not declare an observer,

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

  • is not directly eligible for injection into any javax.enterprise.inject.Instance or javax.inject.Provider injection point

The extensions can eliminate possible false positives by producing UnremovableBeanBuildItem.

1. InjectionPoint.getMember() is currently not supported.