Bean Production and Lookup

Back

Loading concept...

🏭 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! 🚀

Loading story...

Story - Premium Content

Please sign in to view this story and start learning.

Upgrade to Premium to unlock full access to all stories.

Stay Tuned!

Story is coming soon.

Story Preview

Story - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.