Что произойдет в Spring, если несколько бинов реализуют один интерфейс?

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

Ответ

При наличии нескольких бинов, реализующих один интерфейс, Spring по умолчанию не сможет однозначно разрешить зависимость при автоматическом связывании (@Autowired), что приведет к исключению NoUniqueBeanDefinitionException.

Проблема:

interface PaymentService { void pay(); }

@Service
class CreditCardService implements PaymentService { ... }

@Service
class PayPalService implements PaymentService { ... }

@Component
class OrderProcessor {
    @Autowired // ОШИБКА: NoUniqueBeanDefinitionException
    private PaymentService paymentService; // Какой бин выбрать? CreditCardService или PayPalService?
}

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

  1. Использование @Primary: Помечает один бин как предпочтительный по умолчанию.

    @Service
    @Primary // Этот бин будет выбран по умолчанию
    class CreditCardService implements PaymentService { ... }
  2. Использование @Qualifier: Прямое указание имени конкретного бина.

    @Component
    class OrderProcessor {
        @Autowired
        @Qualifier("payPalService") // Ссылается на бин с именем 'payPalService'
        private PaymentService paymentService;
    }
    
    @Service("payPalService") // Явное задание имени бина
    class PayPalService implements PaymentService { ... }
  3. Внедрение всех бинов (коллекции): Если логика требует использования всех реализаций.

    @Component
    class PaymentRouter {
        @Autowired // Внедряется List из всех бинов типа PaymentService
        private List<PaymentService> allPaymentServices;
    }
  4. Использование @Resource (JSR-250): Аналог @Autowired + @Qualifier, но работает по имени.

    @Component
    class OrderProcessor {
        @Resource(name = "creditCardService")
        private PaymentService paymentService;
    }

Выбор стратегии: @Primary подходит для выделения реализации по умолчанию. @Qualifier обеспечивает точный контроль и необходим, когда контекст определяет, какая реализация должна быть использована.