← Назад к вопросам

Как использовать выборочно два 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<>Выбор по ключуНужна логика маршрутизации
Собственная аннотацияПереиспользуемо и читаемоНужно написать аннотацию

Рекомендации

  1. По умолчанию используй @Qualifier — это самый явный и понятный способ
  2. @Primary только для очевидного выбора — когда один Bean явно главный
  3. List<> когда нужны все Bean — для обработки всех реализаций
  4. Map<> для динамического выбора — когда тип выбирается в runtime
  5. Собственная аннотация для переиспользуемого кода — если @Qualifier повторяется часто

Типичная ошибка

// ❌ NoUniqueBeanDefinitionException
@Autowired
private NotificationService notifier; // Spring не знает, какой из двух выбрать

// ✅ Правильно
@Autowired
@Qualifier("emailService")
private NotificationService notifier;
Как использовать выборочно два Bean одного типа в контексте Spring? | PrepBro