Как в Spring разрешить неоднозначность при внедрении бинов (Autowired)?

Ответ

Когда Spring находит несколько бинов, подходящих для инъекции, возникает неоднозначность. Её можно разрешить несколькими способами.

Основные методы:

  1. Аннотация @Qualifier: Прямое указание имени конкретного бина.

    @Component("firstService")
    public class FirstServiceImpl implements MyService {}
    
    @Component("secondService")
    public class SecondServiceImpl implements MyService {}
    
    @Service
    public class ClientService {
        @Autowired
        @Qualifier("secondService") // Явно указываем, какой бин внедрить
        private MyService service;
    }
  2. Аннотация @Primary: Помечает один из кандидатов как бин по умолчанию.

    @Component
    @Primary // Этот бин будет выбран, если нет других указаний
    public class PrimaryServiceImpl implements MyService {}
    
    @Component
    public class SecondaryServiceImpl implements MyService {}
  3. Имя поля/параметра: Если имя поля совпадает с именем одного из бинов (при использовании @Autowired).

    @Component
    public class MyServiceImpl implements MyService {}
    
    @Service
    public class ClientService {
        @Autowired
        private MyService myServiceImpl; // Будет внедрен бин с именем 'myServiceImpl'
    }
  4. Кастомная аннотация: Создание своей аннотации, мета-аннотированной @Qualifier.

    @Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Qualifier
    public @interface DatabaseType {
        String value();
    }
    
    @Component
    @DatabaseType("mysql")
    public class MySqlService implements DataService {}
    
    @Service
    public class BusinessService {
        @Autowired
        @DatabaseType("mysql")
        private DataService dataService;
    }

Ответ 18+ 🔞

А, ну это же классика, ёпта! Вечный геморрой, когда Spring, как слепой котёнок, тычется носом в несколько одинаковых бинов и не знает, какой ему впендюрить. "No qualifying bean of type... available: expected single matching bean but found 2". Знакомо, сука? Чувствуешь, как волнение ебать начинает подкатывать?

Ну ладно, не кипятись. Есть несколько проверенных способов этого распиздяйства избежать. Смотри сюда.

Основные методы, чтобы не было мути:

  1. Аннотация @Qualifier — твой прямой указатель, блядь. Ты просто берёшь и говоришь: "Эй, дурачок, не тупи, вот конкретно этот бин мне и тащи". Как палецем тычешь.

    @Component("firstService")
    public class FirstServiceImpl implements MyService {}
    
    @Component("secondService")
    public class SecondServiceImpl implements MyService {}
    
    @Service
    public class ClientService {
        @Autowired
        @Qualifier("secondService") // Смотри сюда, мудак! Бери того, который 'secondService'!
        private MyService service;
    }
  2. Аннотация @Primary — это типа "любимчик". Один бин получает корону, и его всегда выбирают первым, если других указаний нет. Удобно, но опасненько, если забыть, кто тут главный.

    @Component
    @Primary // Этот парень — царь и бог. Его и тащи по умолчанию, если не сказано иначе.
    public class PrimaryServiceImpl implements MyService {}
    
    @Component
    public class SecondaryServiceImpl implements MyService {} // Этот будет сидеть и курить в сторонке.
  3. Имя поля/параметра — магия, на которую лучше не рассчитывать. Если ты назовёшь поле так же, как называется один из бинов, Spring иногда догадается. Но это, блядь, как ходить по тонкому льду — вроде работает, а потом раз — и пиздец, не работает.

    @Component
    public class MyServiceImpl implements MyService {} // Бин зовут 'myServiceImpl'
    
    @Service
    public class ClientService {
        @Autowired
        private MyService myServiceImpl; // Опа-на! Имя совпало! Может, пронесёт...
    }
  4. Кастомная аннотация — для пафосных и умных. Когда обычный @Qualifier тебе кажется слишком простым, ты создаёшь свою собственную, блядь, аннотацию. Выглядит охуенно солидно, хотя по сути — та же самая хуйня, но в профиль.

    @Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Qualifier // Главное — не забыть эту приписку, а то будет пизда.
    public @interface DatabaseType {
        String value();
    }
    
    @Component
    @DatabaseType("mysql")
    public class MySqlService implements DataService {}
    
    @Service
    public class BusinessService {
        @Autowired
        @DatabaseType("mysql") // Во, теперь ясно — только MySQL, никаких этих ваших Postgres'ов!
        private DataService dataService;
    }

Вот и вся наука, блядь. Выбирай, что душе угодно. Главное — не оставляй Spring'а в подвешенном состоянии, а то он тебе такую неоднозначность устроит, что мало не покажется. В рот меня чих-пых!