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 .