Перейти к основному содержимому

Аннотация Spring @Qualifier

· 4 мин. чтения

1. Обзор

В этом руководстве мы рассмотрим, чем может помочь аннотация @Qualifier , какие проблемы она решает и как ее использовать.

Мы также объясним, чем она отличается от аннотации @Primary и от автосвязывания по имени.

2. Autowire Необходимость устранения неоднозначности

Аннотация @Autowired — отличный способ сделать необходимость внедрения зависимости в Spring явной. Хотя это полезно, есть случаи использования, для которых одной этой аннотации недостаточно, чтобы Spring понял, какой компонент нужно внедрить.

По умолчанию Spring разрешает автосвязанные записи по типу.

Если в контейнере доступно более одного bean-компонента одного типа, фреймворк выдаст NoUniqueBeanDefinitionException , указывая на то, что для автоматического связывания доступно более одного bean-компонента.

Давайте представим ситуацию, в которой существуют два возможных кандидата для внедрения Spring в качестве соавторов компонента в данном экземпляре:

@Component("fooFormatter")
public class FooFormatter implements Formatter {

public String format() {
return "foo";
}
}

@Component("barFormatter")
public class BarFormatter implements Formatter {

public String format() {
return "bar";
}
}

@Component
public class FooService {

@Autowired
private Formatter formatter;
}

Если мы попытаемся загрузить FooService в наш контекст, среда Spring выдаст исключение NoUniqueBeanDefinitionException . Это связано с тем, что Spring не знает, какой компонент следует внедрить . Чтобы избежать этой проблемы, есть несколько решений; аннотация @Qualifier — одна из них.

3. Аннотация @Qualifier

Используя аннотацию @Qualifier , мы можем устранить проблему, связанную с тем, какой bean-компонент необходимо внедрить .

Давайте вернемся к нашему предыдущему примеру, чтобы увидеть, как мы решаем проблему, включив аннотацию @Qualifier , чтобы указать, какой компонент мы хотим использовать:

public class FooService {

@Autowired
@Qualifier("fooFormatter")
private Formatter formatter;
}

Включив аннотацию @Qualifier вместе с именем конкретной реализации, которую мы хотим использовать, в этом примере Foo, мы можем избежать двусмысленности, когда Spring находит несколько bean-компонентов одного типа.

Мы должны принять во внимание, что используемое имя квалификатора — это имя, объявленное в аннотации @Component .

Обратите внимание, что мы могли бы также использовать аннотацию @Qualifier для классов, реализующих Formatter , вместо указания имен в их аннотациях @Component , чтобы получить тот же эффект:

@Component
@Qualifier("fooFormatter")
public class FooFormatter implements Formatter {
//...
}

@Component
@Qualifier("barFormatter")
public class BarFormatter implements Formatter {
//...
}

4. @Qualifier vs @Primary

Есть еще одна аннотация, называемая @Primary , которую мы можем использовать, чтобы решить, какой bean-компонент внедрить, когда присутствует двусмысленность в отношении внедрения зависимостей.

Эта аннотация определяет предпочтение, когда присутствует несколько bean-компонентов одного типа . Компонент, связанный с аннотацией @Primary , будет использоваться, если не указано иное.

Давайте посмотрим пример:

@Configuration
public class Config {

@Bean
public Employee johnEmployee() {
return new Employee("John");
}

@Bean
@Primary
public Employee tonyEmployee() {
return new Employee("Tony");
}
}

В этом примере оба метода возвращают один и тот же тип Employee . Компонент, который Spring внедрит, будет возвращаться методом tonyEmployee . Это потому, что он содержит аннотацию @Primary . Эта аннотация полезна, когда мы хотим указать, какой bean-компонент определенного типа следует внедрять по умолчанию .

Если нам потребуется другой компонент в какой-то точке внедрения, нам нужно будет специально указать это. Мы можем сделать это с помощью аннотации @Qualifier . Например, мы можем указать, что хотим использовать bean-компонент, возвращаемый методом johnEmployee , используя аннотацию @Qualifier .

Стоит отметить, что если присутствуют обе аннотации @Qualifier и @Primary , аннотация @Qualifier будет иметь приоритет. По сути, @Primary определяет значение по умолчанию, а @Qualifier очень специфичен.

Давайте посмотрим на другой способ использования аннотации @Primary , на этот раз на исходном примере:

@Component
@Primary
public class FooFormatter implements Formatter {
//...
}

@Component
public class BarFormatter implements Formatter {
//...
}

В этом случае аннотация @Primary помещается в один из реализующих классов и устраняет неоднозначность сценария.

5. @Qualifier против автоматического связывания по имени

Еще один способ выбрать между несколькими bean-компонентами при автоподключении — использовать имя поля для внедрения. Это значение по умолчанию, если нет других подсказок для Spring . Давайте посмотрим на код, основанный на нашем исходном примере:

public class FooService {

@Autowired
private Formatter fooFormatter;
}

В этом случае Spring определит, что bean-компонент для внедрения — это FooFormatter , поскольку имя поля соответствует значению, которое мы использовали в аннотации @Component для этого bean-компонента.

6. Заключение

В этой статье мы описали сценарии, в которых нам нужно устранить неоднозначность, какие bean-компоненты вводить. В частности, мы рассмотрели аннотацию @Qualifier и сравнили ее с другими подобными способами определения того, какие bean-компоненты необходимо использовать.

Как обычно, полный код для этой статьи доступен на GitHub .