Питання 27 · Розділ 5

Що робити, якщо є кілька бінів одного типу?

Якщо Spring знаходить кілька бінів одного типу → помилка!

Мовні версії: English Russian Ukrainian

🟢 Junior Level

Якщо Spring знаходить кілька бінів одного типу → помилка!

Рішення:

1. @Primary — головний бін:

@Primary
@Bean
public PaymentService defaultPayment() { ... }

2. @Qualifier — явний вибір:

@Autowired
@Qualifier("stripePayment")
private PaymentService payment;

3. Ім’я змінної:

@Autowired
private PaymentService stripePayment;  // За ім'ям біна!

🟡 Middle Level

Порядок вирішення

1. @Qualifier — явний фільтр за ім'ям (найвищий пріоритет)
2. @Primary — глобальна мітка «за замовчуванням»
3. Ім'я змінної (byName) — якщо немає @Qualifier та @Primary
4. @Priority (JSR-250) — числовий пріоритет на рівні класів

Впровадження всіх бінів

// Усі реалізації PaymentService
@Autowired
List<PaymentService> allPayments;

// Map: ім'я біна → бін
@Autowired
Map<String, PaymentService> paymentMap;

// Використання:
PaymentService service = paymentMap.get("stripe");

ObjectProvider

@Autowired
ObjectProvider<PaymentService> provider;

PaymentService service = provider.getIfAvailable(
    () -> new DefaultPaymentService()  // Фоллбек!
);

🔴 Senior Level

Кастомні кваліфікатори

@Qualifier
@Retention(RUNTIME)
public @interface StripePayment { }

@StripePayment
@Bean
public PaymentService stripePayment() { ... }

@Autowired
@StripePayment
private PaymentService payment;

// → Типобезпечно, немає одруків!

NoUniqueBeanDefinitionException діагностика

// FailureAnalyzer Spring Boot автоматично покаже всі знайдені біни
// при NoUniqueBeanDefinitionException — не потрібно лізти в стек-трейс.

Production Experience

Реальний сценарій: 10 реалізацій

Патерн Стратегія:
  → Map<String, Strategy> strategies
  → Вибір за ключем в рантаймі
  → Без if/else!

Best Practices

  1. @Primary → один головний бін
  2. @Qualifier → явний вибір
  3. Map → патерн Стратегія
  4. Кастомні кваліфікатори → типобезпечно
  5. ObjectProvider → фоллбек
  6. @ConditionalOnMissingBean → в стартерах

Резюме для Senior

  • @Primary → бін за замовчуванням
  • @Qualifier → точний вибір
  • Map → усі біни, вибір за ключем
  • Кастомні кваліфікатори → типобезпечно
  • ObjectProvider → ліниве + фоллбек
  • FailureAnalyzer → діагностика помилки

🎯 Шпаргалка для інтерв’ю

Обов’язково знати:

  • Порядок вирішення: @Qualifier → @Primary → byName → @Priority (JSR-250)
  • @Primary — глобальна мітка «бін за замовчуванням» для даного типу
  • @Qualifier — явний фільтр за ім’ям, найвищий пріоритет
  • Впровадження всіх бінів: List (порядок) або Map<String, T> (ім'я → бін) — патерн Стратегія
  • ObjectProvider.getIfAvailable(fallback) — ліниве отримання з фоллбеком
  • Кастомні кваліфікатори (@Qualifier + мета-анотація) — типобезпечно, без рядків
  • FailureAnalyzer Spring Boot автоматично показує всі знайдені біни при NoUniqueBeanDefinitionException

Часті уточнюючі запитання:

  • Що має пріоритет: @Primary чи @Qualifier? — @Qualifier, явний вибір завжди пріоритетніший.
  • Як реалізувати патерн Стратегія в Spring? — Map<String, Strategy> — ключ = ім’я біна, вибір в рантаймі без if/else.
  • Що буде, якщо немає ні @Primary, ні @Qualifier, ні збігу за ім’ям? — NoUniqueBeanDefinitionException.
  • Навіщо потрібні кастомні кваліфікатори? — Типобезпечність: компілятор ловить помилки, немає одруків у рядках.

Червоні прапорці (НЕ говорити):

  • «@Primary завжди перевизначить @Qualifier» — ні, @Qualifier має найвищий пріоритет.
  • «Map впроваджує тільки один бін» — ні, Map містить ВСІ біни типу, ключ = ім’я.
  • «Можна сміливо використовувати рядки в @Qualifier» — ризиковано: одруки ловляться тільки в рантаймі.
  • «Якщо є @Primary, інших бінів цього типу не буде» — ні, усі біни зареєстровані, @Primary тільки вибирає default.

Пов’язані теми:

  • [[28. Що таке @Qualifier]]
  • [[26. Що робить анотація @Autowired]]
  • [[24. Що таке @Configuration клас]]
  • [[25. В чому різниця між @Component, @Service, @Repository, @Controller]]