Как Spring разрешает автозавязывание (`@Autowired`) интерфейса, для которого существует несколько реализаций-бинов?

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

Ответ

Поведение зависит от конфигурации:

  1. Если одна реализация помечена @Primary — будет выбран этот бин.
  2. Если нет @Primary, но имя поля совпадает с именем одного из бинов — может быть выбран этот бин (но это поведение не всегда однозначно).
  3. В остальных случаях — Spring выбросит NoUniqueBeanDefinitionException.

Пример проблемы и решений:

public interface PaymentService {
    void process();
}

@Service("cardService")
public class CardPaymentService implements PaymentService { /*...*/ }

@Service("paypalService")
public class PayPalPaymentService implements PaymentService { /*...*/ }

@Component
public class OrderProcessor {
    @Autowired // ОШИБКА: NoUniqueBeanDefinitionException
    private PaymentService paymentService;
}

Способы разрешения:

  • Использование @Primary:
    @Service
    @Primary // Теперь этот бин будет выбран по умолчанию
    public class CardPaymentService implements PaymentService { /*...*/ }
  • Использование @Qualifier (предпочтительно для ясности):
    @Component
    public class OrderProcessor {
        @Autowired
        @Qualifier("paypalService") // Явно указываем, какую реализацию использовать
        private PaymentService paymentService;
    }
  • Инжекция Map или List всех реализаций:
    @Autowired
    private Map<String, PaymentService> allPaymentServices; // Ключ — имя бина