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

Что будет при старте двух бинов с одинаковым типом?

2.0 Middle🔥 181 комментариев
#Spring Boot и Spring Data#Spring Framework

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Spring: Два Bean одного типа

Вопрос: Что будет при старте если есть два Bean одинакового типа?

Ответ: NoUniqueBeanDefinitionException при инъекции зависимостей.

Проблема: Ambiguity (неоднозначность)

@Configuration
public class AppConfig {
    @Bean
    public UserRepository mysqlRepository() {
        return new MySqlUserRepository();
    }
    
    @Bean
    public UserRepository postgresRepository() {
        return new PostgresUserRepository();
    }
}

@Service
public class UserService {
    @Autowired
    private UserRepository userRepository; // ❌ ОШИБКА!
    // Spring не знает какой Bean использовать
}

// Ошибка при старте:
// NoUniqueBeanDefinitionException: No qualifying bean of type
// 'UserRepository' available: expected single matching bean but found 2:
// mysqlRepository, postgresRepository

Решение 1: @Qualifier

@Service
public class UserService {
    @Autowired
    @Qualifier("mysqlRepository") // Указываем какой Bean использовать
    private UserRepository userRepository;
}

Решение 2: @Primary

@Configuration
public class AppConfig {
    @Bean
    @Primary // Этот используется по умолчанию
    public UserRepository mysqlRepository() {
        return new MySqlUserRepository();
    }
    
    @Bean
    public UserRepository postgresRepository() {
        return new PostgresUserRepository();
    }
}

@Service
public class UserService {
    @Autowired
    private UserRepository userRepository; // Будет mysqlRepository
}

Решение 3: Использовать разные типы

@Configuration
public class AppConfig {
    @Bean
    public MySqlUserRepository mysqlRepository() {
        return new MySqlUserRepository();
    }
    
    @Bean
    public PostgresUserRepository postgresRepository() {
        return new PostgresUserRepository();
    }
}

@Service
public class UserService {
    @Autowired
    private MySqlUserRepository mysqlRepo; // ✓ Одна реализация
    
    @Autowired
    private PostgresUserRepository postgresRepo; // ✓ Другая реализация
}

Решение 4: @Autowired(required = false)

@Service
public class UserService {
    @Autowired(required = false) // Опционально
    private UserRepository userRepository;
    
    public void process() {
        if (userRepository != null) {
            userRepository.findAll();
        }
    }
}

Когда Spring выбрасывает исключение

Ууровень ошибки: При инъекции зависимостей

Если есть два Bean типа UserRepository:
@Autowired private UserRepository repo; → NoUniqueBeanDefinitionException

Если нет Bean типа UserRepository:
@Autowired private UserRepository repo; → NoSuchBeanDefinitionException

Если есть ровно один Bean:
@Autowired private UserRepository repo; → ✓ OK

Пример с интерфейсом и несколькими реализациями

// Интерфейс
public interface PaymentProcessor {
    void process(Payment payment);
}

// Реализация 1
@Component
public class StripePaymentProcessor implements PaymentProcessor {
    @Override
    public void process(Payment payment) {
        System.out.println("Processing with Stripe");
    }
}

// Реализация 2
@Component
public class PayPalPaymentProcessor implements PaymentProcessor {
    @Override
    public void process(Payment payment) {
        System.out.println("Processing with PayPal");
    }
}

// Сервис с неоднозначностью
@Service
public class OrderService {
    @Autowired
    private PaymentProcessor processor; // ❌ Какой использовать?
}

// Решение с @Qualifier
@Service
public class OrderService {
    @Autowired
    @Qualifier("stripePaymentProcessor")
    private PaymentProcessor processor; // ✓ Используем Stripe
}

Получить все Bean одного типа

@Service
public class PaymentService {
    @Autowired
    private List<PaymentProcessor> processors; // ✓ Получить ВСЕ реализации
    
    public void processPayment(Payment payment) {
        for (PaymentProcessor processor : processors) {
            processor.process(payment);
        }
    }
}

// Или как Map
@Service
public class PaymentService {
    @Autowired
    private Map<String, PaymentProcessor> processorMap;
    
    public void processWithMethod(String method, Payment payment) {
        PaymentProcessor processor = processorMap.get(method);
        if (processor != null) {
            processor.process(payment);
        }
    }
}

Использование Custom Qualifier

// Custom аннотация
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Database {
    String value();
}

// Применение
@Component
@Database("mysql")
public class MySqlRepository implements UserRepository {
    // ...
}

@Component
@Database("postgres")
public class PostgresRepository implements UserRepository {
    // ...
}

// Использование
@Service
public class UserService {
    @Autowired
    @Database("mysql")
    private UserRepository repository;
}

@Conditional для проф репозитория

@Configuration
public class RepositoryConfig {
    @Bean
    @ConditionalOnProperty(name = "db.type", havingValue = "mysql")
    public UserRepository mysqlRepository() {
        return new MySqlUserRepository();
    }
    
    @Bean
    @ConditionalOnProperty(name = "db.type", havingValue = "postgres")
    public UserRepository postgresRepository() {
        return new PostgresUserRepository();
    }
}

// application.yml
db:
  type: mysql  # Будет создан только mysqlRepository

На собеседовании

Правильный ответ:

"При старте Spring выбросит NoUniqueBeanDefinitionException если есть два Bean одинакового типа и приложение пытается их инъектировать без указания какой использовать.

Solving strategies:

  1. @Qualifier("beanName") — указать конкретный Bean
  2. @Primary — отметить один Bean как основной
  3. @Conditional — использовать разные Bean в зависимости от условий
  4. List<Type> или Map<String, Type> — получить все реализации
  5. Использовать разные типы вместо одного интерфейса

Также можно использовать @Resource(name="name") как альтернатива @Qualifier."

Ключевые выводы

  • NoUniqueBeanDefinitionException при неоднозначности
  • @Qualifier — явное указание нужного Bean
  • @Primary — основной Bean по умолчанию
  • List<Type> — получить все реализации
  • @Conditional — для профиль-зависимых Bean
  • Custom @Qualifier — для типобезопасного выбора
  • Это частая ошибка при работе с интерфейсами
Что будет при старте двух бинов с одинаковым типом? | PrepBro