🧩💉 CONTEXT & DEPENDENCY INJECTION IN JAKARTA EE (CDI) — THE PRACTICAL GUIDE
🧩💉 CONTEXT & DEPENDENCY INJECTION IN JAKARTA EE (CDI) — THE PRACTICAL GUIDE
If you’re building Jakarta EE apps, CDI (Contexts and Dependency Injection) is the “glue” that makes your code modular, testable, and clean.
Here’s the mental model + the key building blocks you’ll use daily. 👇
🔸 TLDR ✅
▪️ Beans are managed objects (CDI creates them).
▪️ @Inject wires dependencies without new.
▪️ Qualifiers disambiguate “which implementation?”.
▪️ Scopes control lifecycle (request, session, app…).

🔸 NAMED BEANS 🏷️
A named bean is a CDI bean that also has a name you can reference (commonly in UI layers like JSF).
▪️ @Named exposes the bean under a name (default: class name with lowercase first letter)
▪️ Useful when a template / UI expression needs a reference
import jakarta.inject.Named; import jakarta.enterprise.context.RequestScoped; @Named @RequestScoped public class CheckoutBean { public String status() { return "OK"; } }
🔸 DEPENDENCY INJECTION 💉
Instead of manually creating dependencies, you ask CDI to provide them.
▪️ Use @Inject to request a dependency
▪️ CDI selects a matching bean and manages its lifecycle
▪️ Your code becomes easier to test and refactor
import jakarta.inject.Inject; public class OrderService { @Inject PaymentGateway gateway; public void pay() { gateway.charge(); } }
🔸 QUALIFIERS 🎯
When multiple beans match the same type, CDI needs help choosing the right one.
▪️ Create a custom @Qualifier
▪️ Annotate both the implementation and the injection point
▪️ Makes intent explicit (“use the fast one”, “use the mock one”, etc.)
import jakarta.inject.Qualifier; import java.lang.annotation.Retention; import static java.lang.annotation.RetentionPolicy.RUNTIME; @Qualifier @Retention(RUNTIME) public @interface Premium {}
@Premium public class PremiumPaymentGateway implements PaymentGateway { }
@Inject @Premium PaymentGateway gateway;
🔸 NAMED BEANS SCOPES ⏳
Scope = lifecycle + where the instance “lives”.
Common CDI scopes:
▪️ @Dependent → default; “tied to injection point” (new instance often)
▪️ @RequestScoped → one instance per HTTP request 🌐
▪️ @SessionScoped → one per user session 👤 (must be Serializable)
▪️ @ApplicationScoped → one per app (singleton-ish) 🏢
▪️ @ConversationScoped → longer than request, shorter than session (less used)
Rule of thumb:
▪️ Stateless service → @ApplicationScoped (or @Dependent)
▪️ Web/request data → @RequestScoped
▪️ User state → @SessionScoped (careful with memory)
🔸 CDI EVENTS (FIRING, HANDLING, ORDERING) 📣
Events = publish/subscribe inside your app (decoupled communication).
Fire an event:
import jakarta.enterprise.event.Event; import jakarta.inject.Inject; public class OrderService { @Inject Event<OrderCreated> orderCreated; public void createOrder() { orderCreated.fire(new OrderCreated()); } }
Observe an event:
import jakarta.enterprise.event.Observes; public class AuditListener { public void onOrderCreated(@Observes OrderCreated evt) { // log / audit / metrics } }
Ordering observers:
▪️ Use @Priority to control observer order (lower value runs first)
import jakarta.annotation.Priority; import jakarta.enterprise.event.Observes; public class Listener { public void handle(@Observes @Priority(1000) OrderCreated evt) { } }
Bonus patterns you’ll see:
▪️ Async events with fireAsync(...) (when supported by your CDI version/runtime) ⚡
▪️ Filtering with qualifiers on events (e.g., @Urgent) to target specific listeners 🎯
🔸 TAKEAWAYS 🧠
▪️ CDI is about wiring + lifecycle (not just injection).
▪️ If injection becomes ambiguous → add a qualifier.
▪️ Pick scopes intentionally → it’s a performance & memory decision.
▪️ Events are great for cross-cutting concerns (audit, metrics, notifications) without tight coupling.
#JakartaEE #CDI #DependencyInjection #Java #EnterpriseJava #BackendDevelopment #CleanCode #SoftwareArchitecture #JavaDevelopers #Microservices
Go further with Java certification:
Java👇
Spring👇
SpringBook👇
JavaBook👇