Какой способ внедрения зависимостей (Dependency Injection) в Spring является предпочтительным и почему?

«Какой способ внедрения зависимостей (Dependency Injection) в Spring является предпочтительным и почему?» — вопрос из категории Spring, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Constructor Injection (внедрение через конструктор) является предпочтительным и рекомендованным способом в Spring.

Причины:

  1. Неизменяемость (Immutability): Поля зависимостей можно объявить как final, гарантируя их инициализацию только один раз и потокобезопасность.
  2. Явность и обязательность: Все зависимости явно указаны в конструкторе. Объект не может быть создан в невалидном состоянии с отсутствующими зависимостями.
  3. Упрощение тестирования: Класс легко протестировать в изоляции, передавая моки (Mockito) или заглушки напрямую в конструктор.
  4. Соответствие принципам SOLID: В частности, принципу инверсии зависимостей (Dependency Inversion Principle).

Пример:

@Service
public class OrderService {
    private final PaymentGateway paymentGateway;
    private final InventoryService inventoryService;
    private final NotificationService notificationService;

    // Spring автоматически внедрит зависимости, начиная с версии 4.3+
    public OrderService(PaymentGateway paymentGateway,
                        InventoryService inventoryService,
                        NotificationService notificationService) {
        this.paymentGateway = paymentGateway;
        this.inventoryService = inventoryService;
        this.notificationService = notificationService;
    }
    // Бизнес-логика
}

Альтернативы и их недостатки:

  • Field Injection (полевое): Использование @Autowired над полем. Не рекомендуется, так как скрывает зависимости, усложняет тестирование (требует рефлексии или Spring-контекста) и нарушает инкапсуляцию.
  • Setter Injection (через сеттер): Допустим, но делает зависимость опциональной, что может привести к ошибкам времени выполнения (NullPointerException).