🏭 CDI: The Magic Factory That Builds Your Objects
The Big Picture: What is CDI?
Imagine you’re building with LEGO blocks. Instead of finding each piece yourself, what if a magic helper brought you exactly the pieces you need, already connected? That’s what CDI (Contexts and Dependency Injection) does in Jakarta EE!
CDI is like a super-smart assistant that:
- Creates objects for you automatically
- Connects them together
- Makes sure everyone gets the right helper
🫘 CDI Beans: The Building Blocks
What is a CDI Bean?
A CDI Bean is simply a Java class that CDI can manage. Think of beans like employees in a company. Each employee has a job, and the company (CDI) knows who they are and what they do.
Any regular class can become a CDI Bean!
public class CoffeeMachine {
public String makeCoffee() {
return "Here's your coffee! ☕";
}
}
That’s it! This class is automatically a CDI Bean. CDI sees it and says: “I can manage this!”
What Makes a Good Bean?
A CDI Bean needs:
- A no-argument constructor (or one CDI understands)
- Not be final (CDI needs to extend it sometimes)
- Be a concrete class (not abstract or interface)
// ✅ This is a valid CDI Bean
public class OrderService {
public void placeOrder() {
System.out.println("Order placed!");
}
}
// ❌ This won't work (no concrete class)
public abstract class AbstractService { }
🔍 Bean Discovery Modes: How CDI Finds Your Beans
CDI needs to know which classes to manage. This is called discovery. Think of it like a teacher taking attendance:
graph TD A["Bean Discovery Mode"] --> B["all"] A --> C["annotated"] A --> D["none"] B --> E["Every class is a bean"] C --> F["Only marked classes are beans"] D --> G["CDI is turned off"]
The Three Modes
| Mode | What It Does | Think of It As |
|---|---|---|
| all | Every class becomes a bean | “Everyone’s invited!” |
| annotated | Only classes with special marks | “VIP list only” |
| none | No beans at all | “Party canceled” |
Example: The “annotated” mode needs marks like this:
@ApplicationScoped // This mark says "I'm a bean!"
public class UserService {
public String getUser() {
return "John";
}
}
📄 beans.xml: The Rule Book
The beans.xml file tells CDI how to behave. It lives in the META-INF or WEB-INF folder.
Minimal beans.xml (Recommended)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://jakarta.ee/xml/ns/jakartaee"
version="4.0"
bean-discovery-mode="annotated">
</beans>
What Each Part Means
graph TD A["beans.xml"] --> B["version"] A --> C["bean-discovery-mode"] B --> D["Which CDI version?"] C --> E["all / annotated / none"]
Quick Guide:
- annotated = Only beans with scope annotations (safest, fastest)
- all = Everything is a bean (slower, but easy)
- none = CDI doesn’t work here
💉 CDI Injection Points: Where the Magic Happens
Injection is when CDI automatically gives you an object you need. Instead of creating objects yourself, you just ask for them!
The Old Way (Without CDI) 😓
public class OrderController {
private OrderService service;
public OrderController() {
// You have to create it yourself!
this.service = new OrderService();
}
}
The CDI Way (With Injection) 🎉
public class OrderController {
@Inject
private OrderService service;
// CDI creates and gives you the service!
}
Three Ways to Inject
graph TD A["Injection Points"] --> B["Field Injection"] A --> C["Constructor Injection"] A --> D["Setter Injection"] B --> E["@Inject on field"] C --> F["@Inject on constructor"] D --> G["@Inject on setter method"]
1. Field Injection (Most common)
@Inject
private PaymentService paymentService;
2. Constructor Injection (Best practice)
private final OrderService orderService;
@Inject
public OrderController(OrderService orderService) {
this.orderService = orderService;
}
3. Setter Injection
private NotificationService notifier;
@Inject
public void setNotifier(NotificationService n) {
this.notifier = n;
}
🏷️ CDI Qualifiers: Name Tags for Beans
What if you have two classes that do similar things? How does CDI know which one to use?
Qualifiers are like name tags that tell CDI exactly which bean you want!
The Problem
public interface PaymentProcessor { }
public class CreditCardProcessor
implements PaymentProcessor { }
public class PayPalProcessor
implements PaymentProcessor { }
// CDI is confused! Which one do you want?
@Inject
private PaymentProcessor processor; // ❌ Error!
The Solution: Create a Qualifier
// Step 1: Create name tags
@Qualifier
@Retention(RUNTIME)
@Target({FIELD, TYPE, METHOD, PARAMETER})
public @interface CreditCard { }
@Qualifier
@Retention(RUNTIME)
@Target({FIELD, TYPE, METHOD, PARAMETER})
public @interface PayPal { }
// Step 2: Put name tags on beans
@CreditCard
public class CreditCardProcessor
implements PaymentProcessor { }
@PayPal
public class PayPalProcessor
implements PaymentProcessor { }
// Step 3: Ask for the specific one
@Inject @CreditCard
private PaymentProcessor processor; // ✅ Works!
Built-in Qualifiers
CDI gives you some qualifiers for free:
| Qualifier | What It Means |
|---|---|
@Default |
The normal choice |
@Any |
Matches everything |
@Named |
Give it a string name |
@Inject @Named("fastShipping")
private ShippingService shipping;
🔄 CDI Alternatives: Swapping Beans Like Costumes
Alternatives let you swap one bean for another without changing your code. Perfect for:
- Testing (use a fake database)
- Different environments (dev vs production)
- Feature flags
How It Works
graph TD A["Normal Bean"] --> B["Used by default"] C["Alternative Bean"] --> D["Disabled by default"] E["Enable in beans.xml"] --> F["Alternative replaces Normal!"]
Step 1: Mark the Alternative
// The normal bean
public class RealEmailService {
public void send(String msg) {
// Actually sends email
}
}
// The alternative for testing
@Alternative
public class MockEmailService {
public void send(String msg) {
System.out.println("FAKE: " + msg);
}
}
Step 2: Enable in beans.xml
<beans xmlns="https://jakarta.ee/xml/ns/jakartaee"
version="4.0"
bean-discovery-mode="annotated">
<alternatives>
<class>com.app.MockEmailService</class>
</alternatives>
</beans>
Now MockEmailService replaces RealEmailService everywhere!
Priority Alternative (No XML Needed!)
@Alternative
@Priority(100)
public class MockEmailService {
// This automatically wins!
}
Higher priority number = more important = gets chosen!
🎯 Putting It All Together
Here’s how everything connects:
graph TD A["Your Code"] --> B["Asks for a Bean"] B --> C{CDI Container} C --> D["Checks Qualifiers"] C --> E["Checks Alternatives"] C --> F["Checks Discovery Mode"] D --> G["Injects Right Bean"] E --> G F --> G G --> H["Your Code Works!"]
Complete Example
// 1. The interface
public interface MessageSender {
void send(String message);
}
// 2. Email implementation
@Email // Custom qualifier
@ApplicationScoped
public class EmailSender implements MessageSender {
public void send(String message) {
System.out.println("📧 " + message);
}
}
// 3. SMS implementation
@SMS // Custom qualifier
@ApplicationScoped
public class SmsSender implements MessageSender {
public void send(String message) {
System.out.println("📱 " + message);
}
}
// 4. Using them
@ApplicationScoped
public class NotificationService {
@Inject @Email
private MessageSender emailer;
@Inject @SMS
private MessageSender texter;
public void notifyAll(String msg) {
emailer.send(msg);
texter.send(msg);
}
}
🚀 Quick Recap
| Concept | One-Line Summary |
|---|---|
| CDI Bean | Any class CDI manages for you |
| Discovery Mode | How CDI finds beans (all/annotated/none) |
| beans.xml | Configuration file for CDI rules |
| Injection | CDI gives you objects automatically |
| Qualifiers | Name tags when multiple beans exist |
| Alternatives | Swap beans without code changes |
Remember: CDI is your helpful assistant. You define what you need, and CDI delivers it—automatically, reliably, and magically! ✨
