Short answer
Injection is by type first; if more than one candidate matches, Spring narrows by @Primary (a single default winner), then by @Qualifier name, and finally falls back to matching the injection point's field or parameter name against the bean name.
Explanation
If it still can't disambiguate it throws NoUniqueBeanDefinitionException at startup. In Spring Boot 3 you can also define custom qualifier annotations or inject a List<T>/Map<String,T> to receive all implementations, which is handy for strategy patterns.
Common mistakes
- Not knowing @Primary versus @Qualifier precedence.
- Injecting by field name by accident and depending on it.