Resolution Rules
The complete decision table DDI uses when resolving @Inject — for any combination of implementations, producers and resolvers.
When DDI encounters an @Inject-annotated field, it walks a deterministic
decision tree to pick one class or producer to instantiate. This page is
the exact rule set, lifted from the project README and grouped by the kind of
target you injected.
The rules use four building blocks:
| Concept | What it is |
|---|---|
| Impl | A class implementing the injected interface. |
| Producer | A method or class annotated @Produces, returning the interface or an impl. |
| ClassResolver | A ClassResolver<T> annotated @ResponsibleFor(T.class) — runs on every lookup and returns the class to use. |
| ProducerResolver | A resolver that picks between multiple producers for the same impl. |
When you inject an interface
@Inject Service service;
| Setup | Resolution |
|---|---|
| 0 Impl | Exception — nothing to inject. |
| 0 Impl, 1 Producer for the interface | The producer is used. |
| 1 Impl, 0 Producer | The single impl is used. |
| 1 Impl, 1 Producer for the impl | The producer for the impl is used. |
| 1 Impl, n Producers for the impl | Exception — ambiguous. |
| 1 Impl, 1 Producer for the interface | The producer for the interface is used. |
| 1 Impl, n Producers for the interface | Exception — ambiguous. |
| 1 Impl, 1 Producer for the interface + 1 Producer for the impl | Exception — ambiguous. |
| n Impls, no resolver | Exception — ambiguous. |
| n Impls, 1 responsible ClassResolver | The class returned by the resolver is used. |
| n Impls, 1 Producer for the interface | The producer for the interface is used. |
| n Impls, 1–n Producers for an impl | Exception — ambiguous. |
| n Impls, 1 ClassResolver + 1 Producer for the interface | The producer for the interface is used. |
| n Impls, 1 ClassResolver + 1–n Producers for impls | The class returned by the resolver is used, or its matching producer if one exists. |
| n Impls, n responsible ClassResolvers | Exception — conflicting responsibility. |
| 1 Impl, n Producers for impl, 1 ProducerResolver | The producer selected by the ProducerResolver is used. |
| n Impls, 1 ClassResolver, 0–n Producers per impl, 1 ProducerResolver per impl | The producer selected by the ProducerResolver is used, for the impl selected by the ClassResolver. |
When you inject an implementation directly
@Inject FriendlyGreeter greeter;
| Setup | Resolution |
|---|---|
| Impl, no producer | The impl is used. |
| Impl, 1 producer | The producer is used. |
Reading the table
The two principles behind every row:
- One responsibility per interface. Two
ClassResolvers claiming the same interface is an error, not a merge. Same for ambiguous producer sets. - Producers win over plain instantiation; resolvers win over producers. A
ClassResolverchooses which class; if that class has a producer, the producer runs. AProducerResolveronly enters the picture when multiple producers exist for the same impl.
If your setup hits one of the “Exception” rows, the resolution model is
ambiguous on purpose — DDI refuses to guess. Add a ClassResolver,
ProducerResolver, or remove the redundant producer to make the choice
explicit.
Where the rules apply
These rules run every time DDI resolves an injection point — bootstrap, test, hot-swap, retry. That’s what makes the resolver path useful for pricing-tier routing, feature flags and runtime mocking.