TutorialSpring Boot
Dependency Injection Explained (and Why Spring Gets It Right)
DI is one of the most misunderstood patterns in software. Here's a clear explanation — from the problem it solves to how Spring's IoC container works under the hood.
Chaturmind Team
Dependency Injection Explained
Dependency Injection (DI) is a technique where an object receives its dependencies from an external source instead of creating them itself.
The problem without DI
public class OrderService {
private final EmailService emailService = new EmailService(); // tight coupling
public void placeOrder(Order order) {
// process order
emailService.send(order.getCustomerEmail(), "Order confirmed");
}
}
Problems:
- Can't test
OrderServicewithout also runningEmailService - Can't swap
EmailServicefor anSmsService OrderServicecontrols its own lifecycle
The solution with DI
public class OrderService {
private final NotificationService notificationService; // interface, not impl
public OrderService(NotificationService notificationService) {
this.notificationService = notificationService; // injected from outside
}
}
Now you can inject a real EmailService in production and a MockNotificationService in tests.
Spring's IoC container
Spring's ApplicationContext is the DI container. It:
- Scans for
@Component,@Service,@Repository,@Controller - Instantiates them in the right order based on dependencies
- Injects dependencies via constructor, setter, or field injection
@Service
public class OrderService {
private final NotificationService notificationService;
// Spring injects this — no new() anywhere
public OrderService(NotificationService notificationService) {
this.notificationService = notificationService;
}
}
Constructor vs field injection
Always prefer constructor injection:
- Makes dependencies explicit and mandatory
- Allows the class to be used outside Spring (in unit tests)
- Supports immutable fields (
final)
Field injection (@Autowired private X x) hides dependencies and makes testing harder. Avoid it.