Spring Boot Dependency Injection: A Complete Guide
Spring Boot dependency injection is the backbone of the Spring framework. It's what makes your code testable, maintainable, and loosely coupled.
Key Takeaways
- Dependency injection lets Spring manage object creation and wiring, reducing coupling
- Constructor injection is the preferred approach โ it ensures immutability and makes testing straightforward
- Field injection (
) is convenient but hides dependencies@Autowired - Use
when you have multiple implementations of the same interface@Qualifier - Use
to define a default implementation@Primary
What is Dependency Injection?
Consider this problematic approach:
java
// โ Bad: Class creates its own dependency
public class OrderService {
private final OrderRepository repository = new OrderRepository();
public Order findById(Long id) {
return repository.findById(id);
}
}Here's the Spring way:
java
// โ
Good: Spring injects the dependency
@Service
public class OrderService {
private final OrderRepository repository;
public OrderService(OrderRepository repository) {
this.repository = repository;
}
public Order findById(Long id) {
return repository.findById(id);
}
}The Three Types of Dependency Injection
Constructor Injection (Recommended)
java
@Service
public class PaymentService {
private final PaymentGateway paymentGateway;
private final NotificationService notificationService;
// No @Autowired needed with single constructor
public PaymentService(PaymentGateway paymentGateway,
NotificationService notificationService) {
this.paymentGateway = paymentGateway;
this.notificationService = notificationService;
}
public PaymentResult processPayment(Order order) {
PaymentResult result = paymentGateway.charge(order.getTotal());
if (result.isSuccessful()) {
notificationService.sendConfirmation(order.getCustomerEmail());
}
return result;
}
}Field Injection
java
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private PasswordEncoder passwordEncoder;
public User createUser(String email, String password) {
User user = new User();
user.setEmail(email);
user.setPassword(passwordEncoder.encode(password));
return userRepository.save(user);
}
}Setter Injection
java
@Service
public class ReportService {
private ReportGenerator reportGenerator;
private EmailService emailService;
@Autowired
public void setReportGenerator(ReportGenerator reportGenerator) {
this.reportGenerator = reportGenerator;
}
@Autowired
public void setEmailService(EmailService emailService) {
this.emailService = emailService;
}
public void generateAndSendReport(Long userId) {
Report report = reportGenerator.generate(userId);
emailService.send(report);
}
}Spring Stereotypes
@Component
java
@Component
public class EmailValidator {
private static final Pattern EMAIL_PATTERN =
Pattern.compile("^[A-Za-z0-9+_.-]+@(.+)$");
public boolean isValid(String email) {
return email != null && EMAIL_PATTERN.matcher(email).matches();
}
}@Service
java
@Service
public class InventoryService {
private final ProductRepository productRepository;
private final WarehouseClient warehouseClient;
public InventoryService(ProductRepository productRepository,
WarehouseClient warehouseClient) {
this.productRepository = productRepository;
this.warehouseClient = warehouseClient;
}
public boolean isInStock(Long productId, int quantity) {
Product product = productRepository.findById(productId)
.orElseThrow(() -> new ProductNotFoundException(productId));
int available = warehouseClient.getAvailableQuantity(product.getSku());
return available >= quantity;
}
}@Repository
java
@Repository
public class JpaOrderRepository implements OrderRepository {
private final EntityManager entityManager;
public JpaOrderRepository(EntityManager entityManager) {
this.entityManager = entityManager;
}
@Override
public Optional<Order> findById(Long id) {
Order order = entityManager.find(Order.class, id);
return Optional.ofNullable(order);
}
@Override
public Order save(Order order) {
entityManager.persist(order);
return order;
}
}Handling Multiple Implementations
With @Qualifier
java
@Service
public class AlertService {
private final NotificationSender emailSender;
private final NotificationSender smsSender;
public AlertService(
@Qualifier("emailNotificationSender") NotificationSender emailSender,
@Qualifier("smsNotificationSender") NotificationSender smsSender) {
this.emailSender = emailSender;
this.smsSender = smsSender;
}
public void sendUrgentAlert(String recipient, String message) {
emailSender.send(recipient, message);
smsSender.send(recipient, message);
}
}With @Primary
java
@Component
@Primary
public class EmailNotificationSender implements NotificationSender {
@Override
public void send(String recipient, String message) {
// Email is the default notification method
}
}
@Component
public class SmsNotificationSender implements NotificationSender {
@Override
public void send(String recipient, String message) {
// SMS is used only when explicitly requested
}
}