Почему в Spring внедрение зависимостей через конструктор считается предпочтительным?

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

Ответ

Внедрение зависимостей через конструктор (Constructor Injection) рекомендуется как лучшая практика в Spring по следующим причинам:

Основные преимущества:

  • Неизменяемость (Immutability): Поля зависимостей можно объявить как final. Это гарантирует, что они будут инициализированы один раз при создании бина и не могут быть изменены позже, что способствует потокобезопасности.
  • Гарантированная инициализация: Объект не может быть создан в неполном или невалидном состоянии, так как все обязательные зависимости должны быть переданы в конструкторе.
  • Упрощение тестирования: Класс легко протестировать в изоляции (юнит-тест), просто создав его экземпляр с помощью new и передав mock-зависимости.
  • Обнаружение циклических зависимостей: Spring Framework немедленно обнаружит циклическую зависимость (BeanCurrentlyInCreationException) при запуске контекста, а не в случайный момент времени.
  • Ясность контракта: Сигнатура конструктора явно определяет, какие зависимости необходимы для работы класса.

Пример:

@Service
public class OrderService {
    // Зависимость объявлена как final
    private final OrderRepository orderRepository;
    private final PaymentService paymentService;

    // @Autowired не обязателен для одного конструктора, начиная с Spring 4.3
    public OrderService(OrderRepository orderRepository, PaymentService paymentService) {
        this.orderRepository = orderRepository;
        this.paymentService = paymentService;
    }
    // ... бизнес-логика
}

Начиная с Spring 4.3, если у класса есть только один конструктор, аннотацию @Autowired можно опустить. Внедрение через сеттеры или поля следует использовать только для опциональных зависимостей.