Spring - Dependency Injection

Dependency Injection (DI) is the practical mechanism Spring uses to implement IoC. Instead of objects creating their own dependencies, Spring injects them at runtime. Spring supports three styles of DI: constructor injection, setter injection, and field injection.

Contents

Constructor Injection

Constructor injection provides dependencies through the constructor. This is the recommended approach because it makes dependencies explicit, supports immutability with final fields, and makes the class easy to unit-test.

Java
import org.springframework.stereotype.Service;

@Service
public class OrderService {

    private final PaymentService paymentService;
    private final InventoryService inventoryService;

    // Spring automatically injects beans when there is only one constructor
    public OrderService(PaymentService paymentService, InventoryService inventoryService) {
        this.paymentService = paymentService;
        this.inventoryService = inventoryService;
    }

    public void placeOrder(Order order) {
        inventoryService.reserve(order);
        paymentService.charge(order);
    }
}

Since Spring 4.3, if a class has only one constructor, the @Autowired annotation is optional: Spring uses it automatically.

Setter Injection

Setter injection uses @Autowired on a setter method. This is useful for optional dependencies or when you need to allow the dependency to be changed after object creation.

Java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class NotificationService {

    private EmailService emailService;

    @Autowired
    public void setEmailService(EmailService emailService) {
        this.emailService = emailService;
    }

    public void sendNotification(String message) {
        emailService.send(message);
    }
}

Field Injection

Field injection places @Autowired directly on a field. Although concise, it is not recommended for production code: it hides dependencies, makes unit testing harder, and cannot be used with final fields.

Java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class ReportService {

    @Autowired
    private DataSource dataSource;   // Not recommended

    public Report generateReport() {
        // use dataSource ...
        return new Report();
    }
}

Which injection style to use?