Способы внедрения зависимостей Spring

Constructor injection

Основной вариант для обязательных зависимостей:

@Service
class OrderService {
    private final OrderRepository repository;
    private final Clock clock;
 
    OrderService(OrderRepository repository, Clock clock) {
        this.repository = repository;
        this.clock = clock;
    }
}

Если конструктор один, @Autowired не требуется. Объект можно создать в unit-тесте без контейнера, а поля остаются final.

Setter injection

Подходит для действительно необязательной или изменяемой настройки:

@Autowired(required = false)
void setAuditSink(AuditSink auditSink) {
    this.auditSink = auditSink;
}

Объект некоторое время существует в неполном состоянии, поэтому обязательные зависимости так передавать не стоит.

Field injection

@Autowired
private OrderRepository repository;

Работает, но скрывает контракт класса, мешает final и усложняет простой unit-тест. В прикладном коде лучше конструктор.

Как выбирается кандидат

  1. Сначала совместимость типа.
  2. @Qualifier уточняет имя/метку.
  3. @Primary задаёт предпочтительный bean.
  4. Имя параметра может использоваться как дополнительный вариант, но на него не стоит полагаться вместо явного qualifier.
  5. Коллекция List<Handler> получает все подходящие бины; порядок задают @Order.

Опциональность выражают Optional<T> или ObjectProvider<T>, когда отложенное получение действительно нужно.

Циклические зависимости

Constructor cycle невозможно корректно построить. Это полезный сигнал, что обязанности сцеплены. @Lazy или setter могут отложить проблему, но сначала следует пересмотреть границы сервисов.

Вопросы для повторения

  1. Почему constructor injection предпочтительнее? Зависимости явны, обязательны и удобны для тестов.
  2. Когда нужен @Qualifier? Когда кандидатов одного типа несколько.
  3. Чем ObjectProvider отличается от прямой зависимости? Позволяет получить её позже или многократно.
  4. Почему field injection мешает тесту? Объект нельзя нормально создать обычным конструктором.
  5. Что означает цикл конструкторов? Часто неверное разделение обязанностей.

Связано: IoC контейнер, Виды бинов, Conditional и Profile.