Вопрос 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]]