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

Как зарегистрировать два 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.

Как зарегистрировать два Bean одного типа в контексте Spring | PrepBro