Spring Dependency Injection (DI)
In the previous lesson, you understood the idea of Inversion of Control (IoC). Now we move one step deeper and learn how Spring actually implements IoC — using Dependency Injection (DI).
Dependency Injection is the core mechanism that allows Spring to manage relationships between objects cleanly and efficiently.
What Is Dependency Injection?
Dependency Injection means providing dependencies from the outside instead of creating them inside a class.
A dependency is simply an object that another object needs to function.
In Spring, dependencies are injected by the framework automatically.
Why Dependency Injection Matters
Without DI, Java applications quickly become tightly coupled and hard to maintain.
With DI, classes become:
- Easier to modify
- Easier to test
- More reusable
- Cleaner in design
This is why DI is used in almost every modern enterprise Java application.
Real-World Example Without DI
Imagine an application that sends notifications.
public class EmailService {
public void sendEmail() {
System.out.println("Sending email");
}
}
public class NotificationService {
EmailService emailService = new EmailService();
public void notifyUser() {
emailService.sendEmail();
}
}
Here, NotificationService is tightly coupled to EmailService.
Switching to SMS or WhatsApp requires code changes.
Same Example With Dependency Injection
Now let Spring handle the dependency.
public class NotificationService {
private EmailService emailService;
public NotificationService(EmailService emailService) {
this.emailService = emailService;
}
public void notifyUser() {
emailService.sendEmail();
}
}
The dependency is injected from outside.
Spring decides which implementation to provide.
Types of Dependency Injection in Spring
Spring supports three main types of dependency injection.
- Constructor Injection
- Setter Injection
- Field Injection
Each approach has its use cases.
Constructor Injection (Recommended)
Constructor Injection is the most recommended approach in Spring.
@Component
public class OrderService {
private final PaymentService paymentService;
public OrderService(PaymentService paymentService) {
this.paymentService = paymentService;
}
}
This approach ensures dependencies are available at object creation time.
Setter Injection
Setter Injection injects dependencies using setter methods.
@Component
public class OrderService {
private PaymentService paymentService;
@Autowired
public void setPaymentService(PaymentService paymentService) {
this.paymentService = paymentService;
}
}
This is useful when dependencies are optional.
Field Injection (Not Recommended)
Field Injection injects dependencies directly into fields.
@Component
public class OrderService {
@Autowired
private PaymentService paymentService;
}
While simple, this approach makes testing harder and is not recommended for large systems.
@Autowired Annotation
The @Autowired annotation tells Spring to inject a dependency automatically.
Spring resolves dependencies by type first and then by name if needed.
How Spring Finds Dependencies
Spring scans the application for components using annotations like:
@Component@Service@Repository@Controller
These annotations register classes as Spring beans.
Dependency Injection and Testing
DI makes testing easier because dependencies can be mocked.
This allows developers to test business logic without real database or API calls.
Industry Usage
All modern Spring applications rely heavily on DI.
Microservices, REST APIs, and enterprise systems use DI to stay flexible and scalable.
What Comes Next?
Now that you understand Dependency Injection, the next lesson will show how Spring manages beans using Spring Bean Configuration.
You will see how beans are created, configured, and controlled.