Unravel › Java concepts › Jakarta and other frameworks
CDI: contexts and dependency injection (Jakarta EE)
CDI is the standard dependency injection of Jakarta EE (and Quarkus).
At a glance
Spring is a kitchen manager who stocks stations.
- 1@Inject on a field, constructor or setter requests a bean by typeQualifiers (@Named, custom annotations) pick among several implementations; @Alternative plus @Priority swaps one globally.
- 2Scopes: @ApplicationScoped (one shared instance), @RequestScoped (per HTTP request), @SessionScoped (per user session), @Dependent (new instance per injection point, the default)
- 3Events: Event<OrderPlaced>.fire(event) publishes, void on(@Observes OrderPlaced e) receivesObservers can be transactional (@Observes(during = AFTER_SUCCESS)) and async (@ObservesAsync).
- 4Producers (@Produces) turn the result of a method or field into a bean, the CDI counterpart of Spring @BeanInterceptors (@InterceptorBinding) add cross-cutting behaviour like Spring AOP.
- Normal-scoped beans (application, request, session) are injected as client proxies, so the injected object is a stand-in that finds the real instance of the current scope on each call
- Injecting a @RequestScoped bean into an @ApplicationScoped one is safe because of the proxy; injecting a @Dependent one gives a single instance for the lifetime of the host bean, which surprises people
- At startup the container discovers beans by scanning archives (annotated bean classes with bean-discovery-mode), builds a resolution table by type and qualifiers, and fails the deployment if an injection point has zero or several matches
@ApplicationScopedpublic class OrderNotifier { @Inject MailService mail; void onOrderPlaced(@Observes OrderPlaced event) { mail.sendConfirmation(event.customerEmail()); }}@RequestScopedpublic class Checkout { @Inject Event<OrderPlaced> placed; public void complete(Order order) { placed.fire(new OrderPlaced(order.id(), order.customerEmail())); }}- line 1 uses ApplicationScoped, the core of CDI: contexts and dependency injection (Jakarta EE)
- line 6 uses Observes, the core of CDI: contexts and dependency injection (Jakarta EE)
- line 11 uses RequestScoped, the core of CDI: contexts and dependency injection (Jakarta EE)
Read the full explanation (about 2 min)
CDI is the standard dependency injection of Jakarta EE (and Quarkus). Like Spring beans, classes get what they need injected, but each bean has a scope that says how long it lives, and components talk through typed events.
A mental model
Spring is a kitchen manager who stocks stations. CDI is the same job done by a union rulebook: every item carries a label saying how long it stays on the line (per request, per session, forever) and the cooks announce events on a loudspeaker.
How it works
@Inject on a field, constructor or setter requests a bean by type. Qualifiers (@Named, custom annotations) pick among several implementations; @Alternative plus @Priority swaps one globally.
Scopes: @ApplicationScoped (one shared instance), @RequestScoped (per HTTP request), @SessionScoped (per user session), @Dependent (new instance per injection point, the default).
Events: Event<OrderPlaced>.fire(event) publishes, void on(@Observes OrderPlaced e) receives. Observers can be transactional (@Observes(during = AFTER_SUCCESS)) and async (@ObservesAsync).
Producers (@Produces) turn the result of a method or field into a bean, the CDI counterpart of Spring @Bean. Interceptors (@InterceptorBinding) add cross-cutting behaviour like Spring AOP.
Going deeper
Normal-scoped beans (application, request, session) are injected as client proxies, so the injected object is a stand-in that finds the real instance of the current scope on each call. Beans must therefore be non-final and have a no-argument constructor.
Injecting a @RequestScoped bean into an @ApplicationScoped one is safe because of the proxy; injecting a @Dependent one gives a single instance for the lifetime of the host bean, which surprises people.
Package names: javax.inject and javax.enterprise became jakarta.inject and jakarta.enterprise in Jakarta EE 9. The old and new must not mix on one classpath.
Under the hood
At startup the container discovers beans by scanning archives (annotated bean classes with bean-discovery-mode), builds a resolution table by type and qualifiers, and fails the deployment if an injection point has zero or several matches. Quarkus does this at build time (ArC) so no reflection is needed at runtime.
Common mistakes
- Mixing javax and jakarta imports.
- Final classes or missing no-arg constructor for proxied scopes.
- Assuming
@Dependentshares one instance. - Ambiguous injection points without qualifiers.
How Unravel recognises it
When you paste code, Unravel looks for these signs of cdi: contexts and dependency injection (jakarta ee). Spotting them yourself is a good habit when you read unfamiliar code:
- Annotations:
@ApplicationScoped@RequestScoped@SessionScoped@Dependent@ConversationScoped@Observes@Named@Alternative@Interceptor@InterceptorBinding@Disposes@Specializes@Vetoed@Stereotypeand 6 more - Types:
BeanManagerInjectionPointCDI
Part of a learning path
Related concepts
Builds on this
Frequently asked questions
What is CDI: contexts and dependency injection (Jakarta EE)?
CDI is the standard dependency injection of Jakarta EE (and Quarkus). Like Spring beans, classes get what they need injected, but each bean has a scope that says how long it lives, and components talk through typed events.
What are the most common mistakes with CDI: contexts and dependency injection (Jakarta EE)?
Mixing javax and jakarta imports. Final classes or missing no-arg constructor for proxied scopes. Assuming @Dependent shares one instance. Ambiguous injection points without qualifiers.
How can I see CDI: contexts and dependency injection (Jakarta EE) in real code?
Paste your own Java into Unravel and it will point out where cdi: contexts and dependency injection (jakarta ee) appears, explain how it works at the detail level you choose, and draw the flow of the code around it.