โ† Back to blog
Java Published on 2025-12-24 ยท 12 min read

Spring Boot Dependency Injection: A Complete Guide

Master dependency injection with constructor, field, and setter injection

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 (@Autowired) is convenient but hides dependencies
  • Use @Qualifier when you have multiple implementations of the same interface
  • Use @Primary to define a default implementation

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
    }
}
spring-bootdependency-injectionjavabackendarchitecture

Made with Angular & PrimeNG

ยฉ 2025 Java Angular Blog