🔹 What is Dependency Injection?
Dependency Injection (DI) is a design pattern where the Spring framework manages the creation and injection of objects (dependencies) into a class instead of the class creating them itself.
This helps achieve loose coupling and makes the code more maintainable and testable.
In Spring Boot, DI can be achieved mainly in three ways:
- Constructor Injection (Recommended)
- Setter Injection
- Field Injection
🔹 Example Scenario
Let’s say we’re building a simple notification system.
We have two services:
EmailService
(sends emails)SMSService
(sends SMS)
A NotificationController
depends on these services.
1. Define Service Interfaces
package com.example.demo.service;
public interface MessageService {
void sendMessage(String recipient, String message);
}
2. Implement the Services
package com.example.demo.service;
import org.springframework.stereotype.Service;
@Service
public class EmailService implements MessageService {
@Override
public void sendMessage(String recipient, String message) {
System.out.println("Sending Email to " + recipient + ": " + message);
}
}
package com.example.demo.service;
import org.springframework.stereotype.Service;
@Service
public class SMSService implements MessageService {
@Override
public void sendMessage(String recipient, String message) {
System.out.println("Sending SMS to " + recipient + ": " + message);
}
}
3. Inject Services into a Controller
✅ Constructor Injection (Best Practice)
package com.example.demo.controller;
import com.example.demo.service.MessageService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class NotificationController {
private final MessageService messageService;
// Constructor Injection
public NotificationController(MessageService messageService) {
this.messageService = messageService;
}
@GetMapping("/notify")
public String sendNotification() {
messageService.sendMessage("user@example.com", "Hello from Spring Boot!");
return "Notification Sent!";
}
}
4. Handling Multiple Implementations
If both EmailService
and SMSService
implement MessageService
, Spring won’t know which one to inject.
We solve this with @Qualifier
.
package com.example.demo.controller;
import com.example.demo.service.MessageService;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class NotificationController {
private final MessageService messageService;
public NotificationController(@Qualifier("emailService") MessageService messageService) {
this.messageService = messageService;
}
@GetMapping("/notify")
public String sendNotification() {
messageService.sendMessage("user@example.com", "Hello via Email!");
return "Notification Sent!";
}
}
🔹 Key Points
- Use
@Service
(or@Component
) to mark beans that Spring should manage. - Use constructor injection instead of field injection for immutability and easier testing.
- Use
@Qualifier
when multiple implementations exist. - Spring Boot automatically scans and injects beans in the
@SpringBootApplication
package and its subpackages.