🏭 Bean Production and Lookup in CDI
The Factory That Never Sleeps
Imagine you run a magic bakery. Every morning, customers come in wanting fresh bread, cakes, and cookies. But here’s the twist: you don’t make everything ahead of time. Instead, you have special recipes and helpers that create exactly what each customer needs, exactly when they need it.
CDI Bean Production and Lookup works just like this bakery! It’s a system where:
- Producers are your recipes that create special items
- Disposers clean up when items are no longer needed
- Built-in Beans are ingredients that are always available
- Programmatic Lookup is like asking “Hey, do you have chocolate cake today?”
- Instance Interface is your smart helper who finds anything you need
Let’s explore each one!
🍳 CDI Producers
What Are Producers?
Think of a Producer as a recipe card in our bakery.
Sometimes, you can’t just grab bread off the shelf. Maybe a customer wants:
- Bread made with a secret family recipe
- Bread that changes based on the day of the week
- Bread that comes from another bakery (external source)
A Producer method is a special recipe that CDI uses to create beans that can’t be made the normal way.
When Do You Need a Producer?
| Situation | Example |
|---|---|
| Object from external library | Database connection |
| Complex creation logic | Object needing config |
| Different types at runtime | Logger for each class |
| Non-default constructor | Objects with parameters |
Simple Example
@ApplicationScoped
public class BakeryFactory {
@Produces
@ApplicationScoped
public Oven createOven() {
// The oven comes from
// another factory
Oven oven = new Oven();
oven.setTemperature(350);
return oven;
}
}
Here, @Produces tells CDI: “When someone needs an Oven, use THIS recipe!”
Producer with Qualifiers
What if you have TWO types of ovens?
@Produces
@Named("pizza")
public Oven createPizzaOven() {
Oven oven = new Oven();
oven.setTemperature(500);
return oven;
}
@Produces
@Named("bread")
public Oven createBreadOven() {
Oven oven = new Oven();
oven.setTemperature(375);
return oven;
}
Now you can ask for the specific oven:
@Inject
@Named("pizza")
private Oven pizzaOven;
Producer Fields
Sometimes, the recipe is super simple. You can use a field instead:
@Produces
@ApplicationScoped
private Oven sharedOven = new Oven();
CDI will use this field directly as the bean!
🧹 CDI Disposers
The Cleanup Crew
Back to our bakery. At the end of the day, you need to:
- Turn off the oven
- Close the cash register
- Clean the counters
Disposers are the cleanup crew for your beans!
When a produced bean is no longer needed, CDI calls the disposer method to clean up properly.
Why Disposers Matter
Without cleanup, you might have:
- ❌ Database connections left open
- ❌ Files that never close
- ❌ Memory leaks
With disposers:
- ✅ Resources are properly released
- ✅ Connections are closed
- ✅ Everything stays tidy
Simple Example
@ApplicationScoped
public class DatabaseFactory {
@Produces
public Connection createConnection() {
return Database.open();
}
public void closeConnection(
@Disposes Connection conn) {
conn.close();
System.out.println("Cleaned up!");
}
}
The @Disposes parameter tells CDI: “When this Connection is done, call ME to clean it up!”
Matching Producers and Disposers
graph TD A["Producer creates Bean"] --> B["Bean is used"] B --> C["Bean scope ends"] C --> D["Disposer cleans up"] style A fill:#90EE90 style D fill:#FFB6C1
Important: The disposer must match the producer’s qualifiers!
@Produces
@Named("special")
public Cake makeSpecialCake() {...}
// Must also have @Named("special")
public void eatCake(
@Disposes @Named("special")
Cake cake) {...}
🎁 CDI Built-in Beans
Always Available, Always Ready
Imagine your bakery has basic ingredients that are always stocked:
- Flour ✓
- Water ✓
- Sugar ✓
You don’t order them. They’re just… there.
Built-in Beans are beans that CDI provides automatically. You don’t create them. Just inject and use!
The Built-in Bean Family
| Bean | What It Does |
|---|---|
BeanManager |
The boss of all beans |
Event<T> |
Sends messages between beans |
Instance<T> |
Finds beans dynamically |
InjectionPoint |
Info about where injection happens |
Principal |
Current logged-in user |
Using BeanManager
@Inject
private BeanManager beanManager;
public void checkBeans() {
Set<Bean<?>> beans =
beanManager.getBeans(Oven.class);
System.out.println(
"Found " + beans.size() + " ovens!");
}
Using Event
@Inject
private Event<OrderPlaced> orderEvent;
public void placeOrder(Order order) {
// Do order stuff...
orderEvent.fire(new OrderPlaced(order));
}
Anyone listening for OrderPlaced will be notified!
Using InjectionPoint
This is like asking: “Who is using me right now?”
@Produces
public Logger createLogger(
InjectionPoint ip) {
String className = ip
.getMember()
.getDeclaringClass()
.getName();
return Logger.getLogger(className);
}
Now each class gets its own logger automatically!
🔍 Programmatic Lookup
Finding Beans When You Need Them
Sometimes you don’t know what you need until runtime.
Think of it like this: A customer walks in and says:
“I want something sweet… but I’ll decide AFTER I see what you have!”
Programmatic Lookup lets you find beans at runtime, not at startup.
Why Not Just @Inject?
@Inject |
Programmatic Lookup |
|---|---|
| Decided at compile time | Decided at runtime |
| Always the same bean | Can choose different beans |
| Fails if bean missing | Can check if bean exists |
Using CDI.current()
The simplest way to look up beans:
public void findOven() {
Oven oven = CDI.current()
.select(Oven.class)
.get();
}
Using BeanManager
More powerful but more complex:
@Inject
BeanManager bm;
public Oven findOven() {
Set<Bean<?>> beans =
bm.getBeans(Oven.class);
Bean<?> bean = bm.resolve(beans);
CreationalContext<?> ctx =
bm.createCreationalContext(bean);
return (Oven) bm.getReference(
bean,
Oven.class,
ctx
);
}
🎯 Instance Interface
Your Smart Bean Finder
The Instance interface is like having a smart helper who can:
- Find any bean you describe
- Tell you if it exists
- Get all matching beans
- Switch between beans easily
It’s the recommended way to do programmatic lookup in CDI!
Basic Usage
@Inject
private Instance<Oven> ovens;
public void useOven() {
if (ovens.isResolvable()) {
Oven oven = ovens.get();
oven.bake();
}
}
Instance Methods
graph LR A["Instance Interface"] --> B["get"] A --> C["isResolvable"] A --> D["isAmbiguous"] A --> E["select"] A --> F["iterator"] B --> B1["Returns the bean"] C --> C1["True if exactly one"] D --> D1["True if multiple"] E --> E1["Narrows by qualifier"] F --> F1["Loop through all"]
Finding Multiple Beans
@Inject
private Instance<PaymentProcessor>
processors;
public void listAll() {
for (PaymentProcessor p : processors) {
System.out.println(p.getName());
}
}
Selecting with Qualifiers
@Inject
private Instance<Oven> ovens;
public Oven getPizzaOven() {
return ovens
.select(new NamedLiteral("pizza"))
.get();
}
Checking Before Getting
public void safeGet() {
if (ovens.isUnsatisfied()) {
System.out.println("No oven found!");
return;
}
if (ovens.isAmbiguous()) {
System.out.println("Too many ovens!");
return;
}
// Exactly one - safe to get
Oven oven = ovens.get();
}
Instance vs CDI.current()
| Feature | Instance | CDI.current() |
|---|---|---|
| Type-safe | ✅ Yes | ❌ No |
| Inject anywhere | ✅ Yes | ✅ Yes |
| Qualifier support | ✅ Easy | ⚠️ Manual |
| Iterable | ✅ Yes | ❌ No |
| Recommended | ✅ Yes | ⚠️ Fallback |
🧩 Putting It All Together
Here’s a complete example using everything we learned:
@ApplicationScoped
public class SmartBakery {
// Built-in bean
@Inject
private Event<BreadBaked> breadEvent;
// Instance for lookup
@Inject
private Instance<Oven> ovens;
// Producer for special bread
@Produces
public SpecialBread makeSpecialBread(
InjectionPoint ip) {
return new SpecialBread(
ip.getMember().getName()
);
}
// Disposer to clean up
public void eatBread(
@Disposes SpecialBread bread) {
bread.finish();
}
public void bakeWithBestOven() {
// Programmatic lookup
if (ovens.isResolvable()) {
Oven oven = ovens.get();
oven.bake();
breadEvent.fire(new BreadBaked());
}
}
}
🎉 You Did It!
You now understand how CDI creates and finds beans like a pro!
Quick Recap:
| Concept | Remember As |
|---|---|
| Producers | Recipe cards for special beans |
| Disposers | Cleanup crew |
| Built-in Beans | Always-stocked ingredients |
| Programmatic Lookup | Finding beans at runtime |
| Instance | Your smart bean-finding helper |
You’re ready to build flexible, clean Jakarta EE applications! 🚀
