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:
@ApplicationScoped
public class CounterBean {
@Inject
CounterService counterService; (1)
void onMessage(@Observes Event msg) { (2)
}
}
-
A package-private injection field
-
A package-private observer method
Or constructor injection:
@ApplicationScoped
public class CounterBean {
private final CounterService service;
CounterBean() { (1)
}
@Inject
CounterBean(CounterService service) { (2)
this.service = service;
}
}
-
Dummy constructor needed for CDI beans with a normal scope
-
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
-
-
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()
andcreateInstance()
-
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):
@BuildStep
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.
|
@BuildStep
void setupResourceInjection(BuildProducer<ResourceAnnotationBuildItem> resourceAnnotations, BuildProducer<GeneratedResourceBuildItem> resources) {
resources.produce(new GeneratedResourceBuildItem("META-INF/services/io.quarkus.arc.ResourceReferenceProvider",
JPAResourceReferenceProvider.class.getName().getBytes()));
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.
@BuildStep
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.
@BuildStep
BeanRegistrarBuildItem syntheticBean() {
return new BeanRegistrarBuildItem(new BeanRegistrar() {
@Override
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
:
@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;
}
public void transform(TransformationContext context) {
if (contex.getTarget().asClass().name().toString().equals("com.foo.Bar")) {
context.transform().add(MyInterceptorBinding.class).done();
}
}
});
}
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
:
@BuildStep
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
:
@BuildStep
ContextRegistrarBuildItem customContext() {
return new ContextRegistrarBuildItem(new ContextRegistrar() {
public void register(RegistrationContext registrationContext) {
registrationContext.configure(CustomScoped.class).normal().contextClass(MyCustomContext.class).done();
}
});
}
6. Removing Unused Beans
The container attempts to remove all unused beans during build by default.
This optimization can be disabled by setting quarkus.arc.remove-unused-beans
to none
or 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
orjavax.inject.Provider
injection point
Users can instruct the container to not remove any of their specific beans (even if they satisfy all the rules specified above) by annotating them with io.quarkus.arc.Unremovable
.
Furthermore, extensions can eliminate possible false positives by producing UnremovableBeanBuildItem
.
Finally, Quarkus provides a middle ground for the bean removal optimization where application beans are never removed whether or not they are unused,
while the optimization proceeds normally for non application classes. To use this mode, set quarkus.arc.remove-unused-beans
to fwk
or framework
.