Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Java  Module 25 – Jakarta REST (JAX-RS) and CDI: services beyond Faces

CDI qualifiers, producers and alternatives

Choose between beans of one type with CDI qualifiers, create beans from code with producer methods, and swap implementations with @Alternative and @Priority.

  • Intermediate
  • 24 minutes
  • Examples run with Java 25.0.4 and Python 3.14.8
  • By MySmartCoPilot
Free core, Pro depth: 8 of 24 minutes free
Free: When one type has two beans · Qualifiers, producers and alternatives at a glance · A qualifier for the second gateway. With a pass: 2 in-depth parts and 3 interview answers, about 16 minutes.

What you will learn

  • Choose between several implementations of one type with qualifiers and Instance<T>
  • Create beans from code with producer methods and fields, and clean them up with disposer methods
  • Swap implementations for tests and environments with @Alternative and @Priority

Before you start

What you’ll learn · Before you start

What you will learn

  • Choose between several implementations of one type with qualifiers and Instance<T>
  • Create beans from code with producer methods and fields, and clean them up with disposer methods
  • Swap implementations for tests and environments with @Alternative and @Priority

Before you start

On this page

When two beans share a type, an injection point that asks only for the type is ambiguous, and the CDI container refuses to start. CDI qualifiers settle it: a qualifier is an annotation you put on a bean and on the injection points that want that particular bean. Producers cover a second gap, objects the container cannot build with a constructor, such as a java.time.Clock or a thread pool: a producer method returns them, and a disposer method cleans them up. Alternatives cover a third case, replacing a bean everywhere for a test or an environment without editing the code that injects it: @Alternative marks the stand-in, and @Priority switches it on for the whole application. The runs below use Weld 6.0.4.Final for CDI 4.1 and fictional payment gateways, and their output is recorded.

When one type has two beans

The container picks a bean for an injection point by its type and its qualifiers. A bean that declares no qualifier has @Default, an injection point without one asks for @Default, and every bean also has @Any. Checkout asks for two gateways, one plain and one with the @Upi qualifier, and Resolution starts it in three containers:

payments/src/main/java/com/example/payments/Checkout.java Java
package com.example.payments;

import jakarta.enterprise.context.Dependent;
import jakarta.inject.Inject;

@Dependent
public class Checkout {
    @Inject // no qualifier: the bean with @Default
    PaymentGateway byDefault;

    @Inject
    @Upi // the bean with @Upi
    PaymentGateway instant;

    public String payBoth(String order) {
        return byDefault.pay(order, 1_450) + "; " + instant.pay(order, 320);
    }
}
Two @Default gateways, none, and one of each Python · resolution_session.py
from session import run

# Compiles the project, asks Maven for the class path of Weld SE, then runs Resolution.
run(
    """
cd payments
mvn -q compile dependency:build-classpath -Dmdep.outputFile=cp.txt
java -cp target/classes:$(cat cp.txt) com.example.payments.Resolution
"""
)

Output

# openjdk version "25.0.4.1" LTS · Apache Maven 3.10.0
$ cd payments
$ mvn -q compile dependency:build-classpath -Dmdep.outputFile=cp.txt
$ java -cp target/classes:$(cat cp.txt) com.example.payments.Resolution
card, wallet and upi:
  the container did not start: WELD-001409: Ambiguous dependencies for type PaymentGateway with qualifiers @Default
    at injection point [BackedAnnotatedField] @Inject com.example.payments.Checkout.byDefault
    at com.example.payments.Checkout.byDefault(Checkout.java:0)
    Possible dependencies: 
    - Managed Bean [class com.example.payments.CardGateway] with qualifiers [@Any @Default]
    - Managed Bean [class com.example.payments.WalletGateway] with qualifiers [@Any @Default]
upi only:
  the container did not start: WELD-001408: Unsatisfied dependencies for type PaymentGateway with qualifiers @Default
    at injection point [BackedAnnotatedField] @Inject com.example.payments.Checkout.byDefault
    at com.example.payments.Checkout.byDefault(Checkout.java:0)
  WELD-001475: The following beans match by type, but none have matching qualifiers:
    - Managed Bean [class com.example.payments.UpiGateway] with qualifiers [@Upi @Any]
card and upi:
  card took 1450 rupees for order 1; upi took 320 rupees for order 1

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 resolution_session.py

With the card and wallet gateways both @Default, the start failed with WELD-001409: Ambiguous dependencies and listed both candidates. With only the qualified gateway present, it failed with WELD-001408: Unsatisfied dependencies, and Weld added WELD-001475, which says that a bean matched the type but not the qualifiers, a common cause of this error. Both messages named the injection point, Checkout.byDefault, and both stopped the start-up before any payment code ran. With one gateway of each kind, each field received its own bean.

Qualifiers, producers and alternatives at a glance

CDI resolves an injection point by type, then by qualifiers, then by alternatives and priority, ending in one bean or an error.An injection point:its type and qualifiers(none written: @Default)Every enabled beanof that type: classes,producer methods, fieldsKeep the beans withevery qualifier asked forNone left:unsatisfiedWELD-001408One left:injectedSeveral left:alternatives only,then top @PriorityStill several:ambiguousWELD-001409one remains

How the container chooses the bean for an injection point

Text description of the diagram

A flow from top to bottom.

1. "An injection point: its type and qualifiers (none written: @Default)" leads to 2. "Every enabled bean of that type: classes, producer methods, fields", which leads to 3. "Keep the beans with every qualifier asked for". From there, three arrows: - to "None left: unsatisfied, WELD-001408"; - to "One left: injected"; - to "Several left: alternatives only, then top @Priority". From that box, an arrow labelled "one remains" leads to "One left: injected", and another leads to "Still several: ambiguous, WELD-001409".

The container does this for every injection point when the application starts, so the two errors stop the start-up instead of a request.

Tool What it answers Where it is written Typical use
a qualifier which of several beans of one type? an annotation on the bean and at the injection point two gateways, two late-fee rules
Instance<T> which bean, chosen while the program runs? code that calls select() and get() the payment method a customer picks
a producer method or field how is this object made? a method or field annotated @Produces a Clock, a thread pool, a configuration value
a disposer method how is a produced object cleaned up? a method with a @Disposes parameter closing the pool when the container stops
an alternative with @Priority which bean replaces the usual one? @Alternative and @Priority on the stand-in a fake gateway in tests, a sandbox in staging

A qualifier labels one bean of a type so that an injection point can ask for it by that label. @Named is a built-in qualifier that labels a bean with a string, mainly for Expression Language. Instance<T> looks beans up while the program runs, so the choice can depend on data. A producer method is a method whose return value becomes a bean. A producer field does the same with the value of a field. A disposer method receives a produced object when its context ends, to close it. An alternative is a bean that stays switched off until it is selected. @Priority selects an alternative for the whole application, and the highest priority wins. CDI Full can also select alternatives for one archive in beans.xml. Producers and alternatives take part in the same resolution as every other bean, so their mistakes, too, stop the start-up rather than a request.

A qualifier for the second gateway

A qualifier is an annotation type annotated @Qualifier and kept at run time. Putting it on UpiGateway takes away that bean’s @Default, so only injection points that ask for @Upi receive it:

The qualifier and the qualified bean

payments/src/main/java/com/example/payments/Upi.java

package com.example.payments;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import jakarta.enterprise.util.AnnotationLiteral;
import jakarta.inject.Qualifier;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

/** A qualifier: an annotation that labels one bean of a type, and the injection points that want that bean. */
@Qualifier
@Retention(RUNTIME)
@Target({ TYPE, METHOD, FIELD, PARAMETER })
public @interface Upi {

    /** An instance of the annotation for code that selects beans at run time: new Upi.Literal(). */
    final class Literal extends AnnotationLiteral<Upi> implements Upi {
        private static final long serialVersionUID = 1L;
    }
}

payments/src/main/java/com/example/payments/UpiGateway.java

package com.example.payments;

import jakarta.enterprise.context.ApplicationScoped;

/** Qualified @Upi, so it no longer has @Default: an injection point without a qualifier never receives it. */
@Upi
@ApplicationScoped
public class UpiGateway implements PaymentGateway {
    @Override
    public String name() {
        return "upi";
    }
}

payments/src/main/java/com/example/payments/CardGateway.java

package com.example.payments;

import jakarta.enterprise.context.ApplicationScoped;

/** No qualifier declared, so it has @Default (and @Any, which every bean has). */
@ApplicationScoped
public class CardGateway implements PaymentGateway {
    @Override
    public String name() {
        return "card";
    }
}

The nested Literal class creates instances of the annotation that code can pass around, for the run-time choices of Instance<T>. CDI qualifiers are Java types, so a typing mistake in one is a compile error, while a mistyped @Named string is found only when the container or a page looks the name up; in Java code, prefer qualifiers.

India-specific: UPI

UPI, the Unified Payments Interface run by the National Payments Corporation of India, moves money between bank accounts in real time from a phone. In this lesson UpiGateway is fictional and contacts no payment network; it only stands for “a second way to pay” next to cards.

Check yourself

5 questions about this lesson. Every answer and why it is right is on the page, behind “Show the answer”. Your score stays in this browser.

  1. Question 1 of 5 UpiGateway is annotated @Upi @ApplicationScoped. Which qualifiers does the bean have?

    Choose one answer.

    Show the answer to question 1

    Answer: @Upi and @Any

    Every bean has @Any. A bean gets @Default only when it declares no qualifier other than @Named or @Any, so declaring @Upi removes @Default, and an injection point without a qualifier never receives UpiGateway.

  2. Question 2 of 5 The container holds CardGateway (no qualifier) and UpiGateway (@Upi). Which bean does @Inject PaymentGateway byDefault; receive?

    Choose one answer.

    Show the answer to question 2

    Answer: CardGateway

    An injection point without a qualifier asks for @Default. Only CardGateway has @Default, so it is the one bean left after the qualifiers are compared, as the third container of the recorded run showed.

  3. Question 3 of 5 A second team adds WalletGateway without a qualifier, next to CardGateway. Neither is an alternative. What happens to @Inject PaymentGateway byDefault;?

    Choose one answer.

    Show the answer to question 3

    Answer: The container refuses to start with an ambiguous-dependency error that names the injection point

    Two eligible beans with no alternative among them are an unresolvable ambiguity, which CDI treats as a deployment problem: Weld stopped the start-up with WELD-001409 and listed both candidates.

  4. Question 4 of 5 Which statements about producer and disposer methods are true?

    Choose every answer that is right.

    Show the answer to question 4

    Answer:

    • A disposer method receives a produced object to clean it up, for example to close it
    • A producer can supply objects of classes without a public constructor, such as java.time.Clock
    • The value a producer method returns becomes a bean that other beans can inject

    A producer method or field is a source of beans, so its results take part in the same resolution as other beans and are checked at start-up. A disposer method is the matching clean-up step, and a producer is the usual way to make objects such as a Clock injectable.

  5. Question 5 of 5 FakeGateway is annotated @Alternative @ApplicationScoped, without @Priority, and nothing else selects it. What does it do in the application?

    Choose one answer.

    Show the answer to question 5

    Answer: Nothing; an alternative stays switched off until it is selected

    An alternative must be selected before it is available for injection. @Priority selects it for the whole application; without it, and without another way of selecting it, the alternative is disabled.

Choosing at run time and making beans from code Pro: opens with a Pro, Premium or Ultimate pass

Some choices can only be made while the program runs, and some objects cannot be built by the container at all. This part shows both, with Instance<T> and producers, and reproduces two of their failures with numbers.

In this part:

  • Choosing a gateway at run time with Instance and annotation literals
  • Dependent objects that Instance keeps until you destroy them, measured
  • Producer methods and fields for objects CDI cannot construct
  • Nulls, Optional and the scope of a producer, reproduced
  • Disposer methods that close what producers opened

6 runnable examples (ClockProducer.java, Java, LateFeePolicy.java, Python, ReminderPool.java) · about 4 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Swapping implementations for tests and environments Pro: opens with a Pro, Premium or Ultimate pass

An alternative replaces a bean without touching the code that injects it. This part shows which alternative wins when several are enabled, gives a test a fixed clock with an alternative producer, and ends with an exercise.

In this part:

  • Replacing the default gateway with @Alternative and @Priority, recorded
  • Which alternative wins: priorities, ties and selection compared
  • A fixed clock for tests, with an alternative producer
  • Qualifier, alternative, producer or Instance: the trade-offs
  • Your turn: a capped late fee and a test clock

6 runnable examples (CappedLateFee.java, Clocks.java, FixedClocks.java, Java, LateFeePolicyTest.java, Python, ReturnDesk.java, ReturnDeskTest.java) · about 4 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Interview questions

  1. Fresher to mid · Warm-up

    What do the @Default and @Any qualifiers mean in CDI?

    Every bean has @Any, whatever else it declares, so an injection point or a lookup with @Any can reach every bean of a type. @Default is the qualifier a bean gets when it declares no qualifier of its own other than @Named or @Any, and it is also what an injection point asks for when it names no qualifier. So a plain class such as CardGateway has @Any and @Default, while UpiGateway, annotated @Upi, has @Upi and @Any but no @Default, and an injection point without a qualifier never receives it. @Any is mostly used with Instance<T>: @Inject @Any Instance<PaymentGateway> reaches every gateway, and select() narrows the choice at run time. Writing @Default yourself is rarely needed, but it is allowed, for example to keep a bean the default while it also carries another qualifier.

  2. Mid to senior

    A second team adds another PaymentGateway implementation, and the next release fails at start-up with "WELD-001409: Ambiguous dependencies" at forty injection points. How do you fix it without editing every injection point, and how do you stop it happening again?

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  3. Mid to senior

    An @ApplicationScoped service calls instance.get() on a @Dependent formatter for every request. After three days the heap is full of formatter objects, although the code keeps no reference to them. Explain the cause and fix it.

    Model answer, Pro. First step and follow-ups

  4. Fresher to mid

    A CDI application charges late fees that depend on today's date, and its tests fail around midnight and at month ends. How do you make "today" controllable in tests without adding test-only branches to production code?

    Model answer, Pro. First step and follow-ups

Key takeaways

  • Two beans of one type make an unqualified injection point ambiguous, and the container refuses to start.
  • A qualifier labels a bean and the injection points that want it; a bean without one has @Default, and every bean has @Any.
  • Producer methods and fields make beans of objects CDI cannot construct, and disposer methods clean them up.
  • An alternative replaces a bean everywhere once @Priority selects it; the highest priority wins.
  • Resolution errors stop the start-up, so wiring mistakes never wait for a user to find them.

References

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.