Getting Started
Add DDI to your Maven build, bootstrap the container, and write your first @Inject.
1. Add the dependency
DDI is published to Maven Central. Add the artifact to your build:
<dependency>
<groupId>com.svenruppert</groupId>
<artifactId>ddi</artifactId>
<version>06.01.01</version>
</dependency>
Gradle (Kotlin DSL):
implementation("com.svenruppert:ddi:06.01.01")
DDI targets JDK 21+. The 1.0.x line targets JDK 8 if you need it.
2. Bootstrap the container
DDI scans the classpath for implementations, producers and resolvers. Tell it which package(s) to scan โ typically your application’s root:
import com.svenruppert.ddi.DI;
public class Main {
public static void main(String[] args) {
DI.activatePackages("com.example");
var app = DI.activateDI(App.class);
app.run();
}
}
That’s the whole bootstrap. No XML, no annotation processor, no container
configuration. Repeated calls to activatePackages add more packages to the
scan.
3. Inject something
Field injection uses the standard javax.inject.Inject annotation:
public class App {
@Inject Greeter greeter;
public void run() {
System.out.println(greeter.greet("World"));
}
}
public interface Greeter {
String greet(String name);
}
public class FriendlyGreeter implements Greeter {
public String greet(String name) { return "Hello, " + name + "!"; }
}
Because exactly one implementation of Greeter is on the classpath, DDI picks
FriendlyGreeter automatically. The complete decision matrix โ what happens
with multiple impls, producers and resolvers โ is documented in
Resolution Rules.
4. Make it dynamic
The interesting case is when several implementations exist and you want to
pick one per call. Declare a ClassResolver and DDI will consult it on every
@Inject:
@ResponsibleFor(Greeter.class)
public class GreeterPolicy implements ClassResolver<Greeter> {
@Override
public Class<? extends Greeter> resolve(Class<Greeter> i) {
var ctx = SecurityContext.current();
return ctx.plan() == Plan.ENTERPRISE
? PremiumGreeter.class
: FriendlyGreeter.class;
}
}
That’s the core idea of DDI: the resolver is plain Java. It can read a feature-flag service, a tenant context, a quota counter, the time of day โ whatever your business needs to decide which implementation comes next.
5. Where to go next
- ClassResolver โ the full API, plus
ProducerResolverfor picking between producers. - Tutorial: Pricing-Tier Routing โ the canonical SaaS use case end-to-end.
- Tutorial: Mocking without Mockito โ the same mechanism, applied to tests.
- Resolution Rules โ the precise rules DDI follows.
Loading packages from a file
For larger applications you can list packages in a resource file and point DDI
at it via the com.svenruppert.ddi.packagesfile system property:
# good.packages
com.example.api
com.example.impl
com.example.policy
java -Dcom.svenruppert.ddi.packagesfile=packages.txt -jar app.jar
The file is read first from the classpath, then from the filesystem.