Как Spring подбирает кандидатов для инжекции по имени и типу
Что проверяют
Знаете ли вы реальный алгоритм @Autowired, а не миф «сначала по имени, потом по типу». Следом обычно спрашивают, что будет при двух реализациях одного интерфейса и чем @Resource отличается от @Autowired.
Как отвечать
@Autowired (как и @Inject) работает от типа. Алгоритм в DefaultListableBeanFactory такой:
- поиск по типу: собираются все бины, совместимые с типом поля или параметра, с учётом дженериков;
- фильтр по квалификатору: если стоит
@Qualifier("name")или своя аннотация-квалификатор, остаются только подходящие; - один кандидат: внедряется он. Ноль кандидатов:
NoSuchBeanDefinitionException, если зависимость не необязательная (required = false,Optional,ObjectProvider); - несколько кандидатов: сначала побеждает
@Primary, затем совпадение имени поля или параметра с именем бина, затем@Priority. В Spring 6.2 имя стало сильнее@Priority, раньше было наоборот. Не решилось:NoUniqueBeanDefinitionException.
Имя бина по умолчанию: имя класса с маленькой буквы (paymentServiceImpl) или имя метода @Bean. Для коллекций правила другие: List<PaymentService> получит все реализации с учётом @Order, а Map<String, PaymentService> разложит их по именам бинов.
@Resource из Jakarta ведёт себя наоборот: ищет по имени (атрибут name или имя поля) и только потом откатывается к типу. В Spring 6.2 появилась ещё @Fallback: такой бин берётся, только если других кандидатов нет.
Где спотыкаются
Утверждают, что при совпадении имени тип не важен. Для @Autowired имя лишь выбирает среди бинов, уже подошедших по типу.
Полагаются на имя параметра конструктора и удивляются ошибке после обновления. Начиная со Spring 6.1 имена параметров доступны только при компиляции с флагом -parameters. Spring Boot включает его сам, а в голом Maven или Gradle его легко потерять.
Лечат конфликт, раскидывая @Primary по нескольким бинам. Два @Primary одного типа снова дают NoUniqueBeanDefinitionException, а явный @Qualifier в точке внедрения читается и сопровождается лучше.