← Назад к вопросам
Как зарегистрировать два Bean одного типа в контексте Spring
2.0 Middle🔥 131 комментариев
#Spring Boot и Spring Data#Spring Framework
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Ответ
Регистрация нескольких Bean одного типа в Spring контексте — частая задача. Рассмотрим все способы от простых к сложным.
1. Использование @Qualifier
Мост простой и надёжный способ — именовать Bean и использовать @Qualifier:
// Конфигурация
@Configuration
public class BeanConfig {
@Bean(name = "primaryDataSource")
public DataSource primaryDataSource() {
return new HikariDataSource(createPrimaryConfig());
}
@Bean(name = "secondaryDataSource")
public DataSource secondaryDataSource() {
return new HikariDataSource(createSecondaryConfig());
}
}
// Инжекция
@Service
public class DataService {
private final DataSource primaryDb;
private final DataSource secondaryDb;
public DataService(
@Qualifier("primaryDataSource") DataSource primaryDb,
@Qualifier("secondaryDataSource") DataSource secondaryDb
) {
this.primaryDb = primaryDb;
this.secondaryDb = secondaryDb;
}
}
2. Использование @Primary
Для одного "основного" Bean:
@Configuration
public class DatabaseConfig {
@Bean
@Primary
public DataSource primaryDataSource() {
return new HikariDataSource(primaryConfig());
}
@Bean
public DataSource cacheDataSource() {
return new HikariDataSource(cacheConfig());
}
}
// Инжекция
@Service
public class UserService {
private final DataSource dataSource; // Получит primaryDataSource
private final DataSource cache;
public UserService(
DataSource dataSource, // @Primary автоматически
@Qualifier("cacheDataSource") DataSource cache
) {
this.dataSource = dataSource;
this.cache = cache;
}
}
3. Регистрация через компоненты
Если Bean создаются из аннотированных классов:
// Первая реализация
@Component("redisCache")
public class RedisCacheImpl implements CacheService {
@Override
public void set(String key, String value) { /* ... */ }
}
// Вторая реализация
@Component("memoryCache")
public class MemoryCacheImpl implements CacheService {
@Override
public void set(String key, String value) { /* ... */ }
}
// Использование
@Service
public class CacheManager {
private final CacheService redis;
private final CacheService memory;
public CacheManager(
@Qualifier("redisCache") CacheService redis,
@Qualifier("memoryCache") CacheService memory
) {
this.redis = redis;
this.memory = memory;
}
}
4. Использование Map всех Bean
Получить все Bean одного типа сразу:
@Service
public class PaymentProcessor {
private final Map<String, PaymentGateway> gateways;
public PaymentProcessor(Map<String, PaymentGateway> gateways) {
this.gateways = gateways;
}
public void process(String gatewayName, Payment payment) {
PaymentGateway gateway = gateways.get(gatewayName);
if (gateway != null) {
gateway.process(payment);
}
}
}
// Bean регистрируются по именам
@Component("stripe")
public class StripeGateway implements PaymentGateway { /* ... */ }
@Component("paypal")
public class PayPalGateway implements PaymentGateway { /* ... */ }
@Component("yandex")
public class YandexKassaGateway implements PaymentGateway { /* ... */ }
Примеры использования:
public void payWithCard(Payment payment) {
gateways.get("stripe").process(payment);
}
public void payWithPayPal(Payment payment) {
gateways.get("paypal").process(payment);
}
5. Использование List всех Bean
Получить все Bean в определённом порядке:
@Configuration
public class NotificationConfig {
@Bean
@Order(1)
public NotificationService emailService() {
return new EmailNotificationService();
}
@Bean
@Order(2)
public NotificationService smsService() {
return new SmsNotificationService();
}
@Bean
@Order(3)
public NotificationService pushService() {
return new PushNotificationService();
}
}
// Инжекция List
@Service
public class NotificationManager {
private final List<NotificationService> services;
public NotificationManager(List<NotificationService> services) {
this.services = services; // В порядке @Order
}
public void notifyAll(String message) {
services.forEach(service -> service.send(message));
}
}
6. Комбинированный подход с нестинг конфигурацией
@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.primary")
public DataSourceProperties primaryProperties() {
return new DataSourceProperties();
}
@Bean
public DataSource primaryDataSource(
@Qualifier("primaryProperties") DataSourceProperties props
) {
return props.initializeDataSourceBuilder().build();
}
@Bean
@ConfigurationProperties(prefix = "spring.datasource.secondary")
public DataSourceProperties secondaryProperties() {
return new DataSourceProperties();
}
@Bean
public DataSource secondaryDataSource(
@Qualifier("secondaryProperties") DataSourceProperties props
) {
return props.initializeDataSourceBuilder().build();
}
}
7. Кастомная аннотация
Для удобства создайте собственную аннотацию:
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface ReadOnlyDB {}
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface WriteDB {}
// Использование
@Configuration
public class DBConfig {
@Bean
@WriteDB
public DataSource writeDataSource() { /* ... */ }
@Bean
@ReadOnlyDB
public DataSource readDataSource() { /* ... */ }
}
@Service
public class UserRepository {
private final DataSource write;
private final DataSource read;
public UserRepository(@WriteDB DataSource write, @ReadOnlyDB DataSource read) {
this.write = write;
this.read = read;
}
}
Сравнение подходов
| Подход | Плюсы | Минусы |
|---|---|---|
| @Qualifier | Просто, явно | Много аннотаций |
| @Primary | Минимум кода | Только для одного Bean |
| Map<String, T> | Динамический выбор | Сложнее в поиске |
| List<T> | Обработка всех | Нужна сортировка |
| Custom aннотация | Читаемость, переиспользование | Больше кода |
Практический совет
Используйте @Qualifier для 2-3 Bean — самый читаемый и поддерживаемый способ. Для более сложных сценариев переходите на Map или List.