The common case for injecting dependencies into a class is with constructor injection. This involves annotating a constructor on the class with @Inject. The CDI manager will look for a constructor with the @Inject annotation when creating an instance of the class. When it finds an @Inject-annotated constructor, it will use reflection to find which parameters are required by the constructor, construct or obtain instances of those dependencies, then call the constructor with those dependencies.
public class Spaceship {
private final PropulsionSystem propulsionSystem;
private final NavigationSystem navigationSystem;
@Inject
public Spaceship(PropulsionSystem propulsionSystem, NavigationSystem navigationSystem) {
this.propulsionSystem = propulsionSystem;
this.navigationSystem = navigationSystem;
}
public void launch() throws FlightUnavailableException {
if (propulsionSystem.hasFuel()) {
propulsionSystem.engageThrust();
} else {
throw new FlightUnavailableException("Launch requirements not met. Ship needs fuel.");
}
}
}
Any time a Spaceship instance defined in this manner is created by CDI, it will receive a PropulsionSystem and a NavigationSystem as arguments to its constructor. Because these dependencies are added via the constructor, we have an easy way to provide alternate dependencies in our test harness when unit testing:
public class SpaceshipTest {
private Spaceship systemUnderTest;
private TestPropulsionSystem testPropulsionSystem;
private TestNavigationSystem testNavigationSystem;
@Before
public void setup() {
setupCollaborators();
systemUnderTest = new Spaceship(testPropulsionSystem, testNavigationSystem);
}
@Test
public void launchSequenceEngagesThrustIfFueled() {
//given
testPropulsionSystem.simulateHavingFuel();
//when
systemUnderTest.launch();
//then
testPropulsionSystem.ensureThrustWasEngaged();
}
}