Ответ
Spring использует четкий алгоритм разрешения зависимостей при выполнении автопривязки (autowiring).
Алгоритм выбора бина для поля/параметра, аннотированного @Autowired:
- Поиск всех кандидатов по типу. Spring находит в контексте все бины, тип которых соответствует (является присваиваемым) требуемому типу зависимости (классу или интерфейсу).
- Фильтрация по
@Qualifier. Если указана аннотация@Qualifier, остаются только бины, имя которых (или значение их@Qualifier) совпадает с указанным. - Проверка аннотации
@Primary. Если среди оставшихся кандидатов есть ровно один бин с аннотацией@Primary, он выбирается. - Сопоставление по имени. Если
@Qualifierне указан и нет@Primary, Spring пытается найти бин, имя которого совпадает с именем поля или параметра. - Инъекция всех бинов (для
CollectionилиMap). Если тип зависимости —List<MyType>,Set<MyType>илиMap<String, MyType>, Spring внедрит все подходящие бины. ДляMapключом будет имя бина.
Результаты алгоритма:
- 0 бинов: Выбрасывается
NoSuchBeanDefinitionException, еслиrequired=true(по умолчанию). - 1 бин: Успешная инъекция.
- >1 бин без разрешения неоднозначности: Выбрасывается
NoUniqueBeanDefinitionException.
Пример, демонстрирующий приоритеты:
@Configuration
public class Config {
@Bean
@Primary // Шаг 3: Выбран, если нет @Qualifier
public Formatter primaryFormatter() { return new PrimaryFormatter(); }
@Bean("customFormatter") // Имя бина = "customFormatter"
@Qualifier("custom") // Имеет квалификатор "custom"
public Formatter customFormatter() { return new CustomFormatter(); }
@Bean
public Formatter defaultFormatter() { return new DefaultFormatter(); }
}
@Service
public class MyService {
@Autowired // Шаг 1: Найдено 3 бина типа Formatter.
// Шаг 3: Выбран primaryFormatter(), так как он помечен @Primary.
private Formatter formatter1;
@Autowired
@Qualifier("custom") // Шаг 2: Выбран customFormatter(), так как его @Qualifier совпадает.
private Formatter formatter2;
@Autowired // Шаг 1: Найдено 3 бина.
// Шаг 4: Выбран бин с именем "defaultFormatter" (совпадает с именем поля).
private Formatter defaultFormatter;
}