← Назад к вопросам
Как использовать выборочно два Bean одного типа в контексте Spring?
2.0 Middle🔥 201 комментариев
#Spring Framework
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Как использовать выборочно два Bean одного типа в контексте Spring
Проблема возникает, когда в Spring контексте зарегистрировано несколько Bean одного типа, а Spring не знает, какой из них внедрить при @Autowired. Есть несколько решений.
Решение 1: @Qualifier (основной способ)
// Определяем два Bean одного типа
@Configuration
public class ServiceConfig {
@Bean
@Qualifier("emailService")
public NotificationService emailService() {
return new EmailNotificationService();
}
@Bean
@Qualifier("smsService")
public NotificationService smsService() {
return new SmsNotificationService();
}
}
// Внедряем нужный Bean с помощью @Qualifier
@Service
public class UserManager {
private final NotificationService emailNotifier;
private final NotificationService smsNotifier;
public UserManager(
@Qualifier("emailService") NotificationService emailNotifier,
@Qualifier("smsService") NotificationService smsNotifier
) {
this.emailNotifier = emailNotifier;
this.smsNotifier = smsNotifier;
}
public void notifyUser() {
emailNotifier.send("Welcome!");
smsNotifier.send("Welcome!");
}
}
Решение 2: @Resource (с именем Bean)
@Service
public class UserManager {
@Resource(name = "emailService")
private NotificationService emailNotifier;
@Resource(name = "smsService")
private NotificationService smsNotifier;
}
@Resource ищет Bean по имени, а не по типу, поэтому это очень явный способ.
Решение 3: @Primary (по умолчанию)
@Configuration
public class ServiceConfig {
@Bean
@Primary // Этот Bean будет внедряться по умолчанию
public NotificationService emailService() {
return new EmailNotificationService();
}
@Bean
public NotificationService smsService() {
return new SmsNotificationService();
}
}
@Service
public class UserManager {
@Autowired
private NotificationService notifier; // Получит emailService
@Autowired
@Qualifier("smsService")
private NotificationService smsNotifier; // Явно указываем SMS
}
Решение 4: Внедрение всех Bean в коллекцию
@Service
public class NotificationManager {
private final List<NotificationService> notifiers;
public NotificationManager(List<NotificationService> notifiers) {
this.notifiers = notifiers; // Получит все Bean типа NotificationService
}
public void notifyAll(String message) {
notifiers.forEach(service -> service.send(message));
}
}
Решение 5: Map с названиями Bean
@Service
public class NotificationRouter {
private final Map<String, NotificationService> services;
public NotificationRouter(Map<String, NotificationService> services) {
this.services = services;
// services = {"emailService" -> EmailNotificationService,
// "smsService" -> SmsNotificationService}
}
public void send(String type, String message) {
NotificationService service = services.get(type);
service.send(message);
}
}
Когда вызовешь send("emailService", "msg"), Spring вернёт нужный Bean.
Решение 6: Собственная аннотация
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface EmailQualifier {}
@Configuration
public class ServiceConfig {
@Bean
@EmailQualifier
public NotificationService emailService() {
return new EmailNotificationService();
}
}
@Service
public class UserManager {
@Autowired
@EmailQualifier
private NotificationService notifier;
}
Это элегантнее, чем @Qualifier с строкой.
Сравнение подходов
| Способ | Плюсы | Минусы |
|---|---|---|
| @Qualifier | Явный, простой | Много кода |
| @Primary | Удобно для "по умолчанию" | Может быть запутанно |
| @Resource | Ясно и однозначно | Нужно помнить имена Bean |
| List<> | Получаешь все Bean | Нужна логика выбора |
| Map<> | Выбор по ключу | Нужна логика маршрутизации |
| Собственная аннотация | Переиспользуемо и читаемо | Нужно написать аннотацию |
Рекомендации
- По умолчанию используй @Qualifier — это самый явный и понятный способ
- @Primary только для очевидного выбора — когда один Bean явно главный
- List<> когда нужны все Bean — для обработки всех реализаций
- Map<> для динамического выбора — когда тип выбирается в runtime
- Собственная аннотация для переиспользуемого кода — если @Qualifier повторяется часто
Типичная ошибка
// ❌ NoUniqueBeanDefinitionException
@Autowired
private NotificationService notifier; // Spring не знает, какой из двух выбрать
// ✅ Правильно
@Autowired
@Qualifier("emailService")
private NotificationService notifier;