Какие способы настройки выбора реализации интерфейса в Spring существуют, например профили, condition properties
Что проверяют
Знаете ли вы, как контейнер разрешает неоднозначность, когда у интерфейса несколько бинов, и чем выбор при старте отличается от выбора в рантайме. Типичный уточняющий вопрос: как сделать реализацию по умолчанию, которую можно переопределить.
Как отвечать
Разделите способы на две группы. Первая решает, какие бины вообще попадут в контекст:
@Profile: бин регистрируется только при активном профиле (spring.profiles.active). Поддерживает выражения:@Profile("!prod"),@Profile("dev & local");@ConditionalOnProperty: из Spring Boot, включает бин по значению свойства, напримерhavingValue = "stub", аmatchIfMissing = trueделает вариант дефолтным;@ConditionalOnMissingBeanи@ConditionalOnClass: основа автоконфигурации. Дефолтная реализация создаётся, только если пользователь не объявил свою;- свой
Condition: класс с методомmatches(ConditionContext, AnnotatedTypeMetadata)и@Conditional(MyCondition.class)на бине.
Вторая выбирает среди бинов, уже живущих в контексте:
@Primary: реализация по умолчанию при внедрении по типу. В новых версиях Spring Framework есть и обратная ей@Fallback;@Qualifier: имя бина или своя аннотация-квалификатор в точке внедрения;- коллекция бинов: внедрить
Map<String, PaymentService>и выбирать реализацию по ключу в рантайме. Это стратегия, когда выбор зависит от данных запроса, а не от окружения.
В тестах реализацию подменяют через @MockitoBean, который заменил устаревший @MockBean, или через тестовый профиль.
Где спотыкаются
Ставят @ConditionalOnMissingBean на обычный @Component в своём коде. Условие проверяется в момент регистрации, порядок сканирования не гарантирован, и результат зависит от случая. Надёжно это работает только в классах автоконфигурации.
Используют профили для бизнес-вариантов вроде выбора платёжного провайдера. Профиль описывает окружение, а переключатель функциональности лучше выразить свойством и @ConditionalOnProperty.
Получают NoUniqueBeanDefinitionException и лечат его удалением второй реализации вместо @Primary или @Qualifier.