Как Spring выбирает бин при нескольких реализациях одного интерфейса
Что проверяют
Знаете ли вы порядок разрешения и способы им управлять.
Как отвечать
Сначала Spring ищет кандидатов по типу. Если подходит ровно один, он и внедряется. Если несколько, включается разрешение неоднозначности в таком порядке:
@Primaryна одном из бинов. Он становится выбором по умолчанию;@Qualifier("имя")в точке внедрения. Он сильнее, чем@Primary;- совпадение имени: если имя параметра или поля совпадает с именем бина, берётся он. Это работает, но хрупко: переименование параметра меняет поведение.
Если ничего не помогло, приложение не стартует с ошибкой NoUniqueBeanDefinitionException, и это правильно: лучше упасть на старте, чем внедрить не ту реализацию.
Дополнительные способы управления:
@Profile: разные реализации включаются в разных окружениях, например заглушка платёжного шлюза на тестовом стенде и настоящий клиент в проде. Тогда неоднозначности вообще не возникает, потому что в контексте живёт только одна;- условные аннотации Boot (
@ConditionalOnProperty,@ConditionalOnMissingBean): выбор по настройке или по отсутствию пользовательского бина.
Отдельно стоит назвать приём, который вместо выбора одной реализации использует все сразу: можно внедрить List<Handler> и получить все реализации, а Map<String, Handler> даст их же с именами бинов в качестве ключей. Так удобно реализуется шаблон стратегии: добавили новый обработчик, и он подхватился сам, без правки места использования. Порядок в списке задаётся @Order.
Где спотыкаются
Полагаются на совпадение имени параметра и ломают приложение переименованием.
Ставят @Primary на несколько бинов сразу.
Не знают про внедрение всех реализаций списком и пишут ветвление вручную.
Считают, что @Qualifier слабее @Primary.