ddi
Made in the European Union

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:

ConceptWhat it is
ImplA class implementing the injected interface.
ProducerA method or class annotated @Produces, returning the interface or an impl.
ClassResolverA ClassResolver<T> annotated @ResponsibleFor(T.class) — runs on every lookup and returns the class to use.
ProducerResolverA resolver that picks between multiple producers for the same impl.

When you inject an interface

@Inject Service service;
SetupResolution
0 ImplException — nothing to inject.
0 Impl, 1 Producer for the interfaceThe producer is used.
1 Impl, 0 ProducerThe single impl is used.
1 Impl, 1 Producer for the implThe producer for the impl is used.
1 Impl, n Producers for the implException — ambiguous.
1 Impl, 1 Producer for the interfaceThe producer for the interface is used.
1 Impl, n Producers for the interfaceException — ambiguous.
1 Impl, 1 Producer for the interface + 1 Producer for the implException — ambiguous.
n Impls, no resolverException — ambiguous.
n Impls, 1 responsible ClassResolverThe class returned by the resolver is used.
n Impls, 1 Producer for the interfaceThe producer for the interface is used.
n Impls, 1–n Producers for an implException — ambiguous.
n Impls, 1 ClassResolver + 1 Producer for the interfaceThe producer for the interface is used.
n Impls, 1 ClassResolver + 1–n Producers for implsThe class returned by the resolver is used, or its matching producer if one exists.
n Impls, n responsible ClassResolversException — conflicting responsibility.
1 Impl, n Producers for impl, 1 ProducerResolverThe producer selected by the ProducerResolver is used.
n Impls, 1 ClassResolver, 0–n Producers per impl, 1 ProducerResolver per implThe producer selected by the ProducerResolver is used, for the impl selected by the ClassResolver.

When you inject an implementation directly

@Inject FriendlyGreeter greeter;
SetupResolution
Impl, no producerThe impl is used.
Impl, 1 producerThe producer is used.

Reading the table

The two principles behind every row:

  1. One responsibility per interface. Two ClassResolvers claiming the same interface is an error, not a merge. Same for ambiguous producer sets.
  2. Producers win over plain instantiation; resolvers win over producers. A ClassResolver chooses which class; if that class has a producer, the producer runs. A ProducerResolver only 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.