1. Обзор
Начиная с Spring 2.5, в фреймворке появилась управляемая аннотациями Dependency Injection
. Основная аннотация этой функции — @Autowired
.
Это позволяет Spring разрешать и внедрять сотрудничающие компоненты в наш компонент.
В этом уроке мы сначала рассмотрим, как включить автоподключение и различные
способы автоподключения bean-компонентов. После этого мы поговорим о разрешении конфликтов bean-компонентов с помощью аннотации @Qualifier
, а также о возможных сценариях исключений.
2. Включение аннотаций @Autowired
Платформа Spring обеспечивает автоматическую инъекцию зависимостей. Другими словами, объявив все зависимости bean-компонентов в файле конфигурации Spring, контейнер Spring может автоматически связывать отношения между взаимодействующими bean-компонентами . Это называется автопроводкой Spring bean
.
Чтобы использовать конфигурацию на основе Java в нашем приложении, давайте включим внедрение на основе аннотаций `` для загрузки нашей конфигурации Spring:
@Configuration
@ComponentScan("com.foreach.autowire.sample")
public class AppConfig {}
Кроме того, аннотация <context:annotation-config>
в основном используется для активации аннотаций внедрения зависимостей в XML-файлах Spring.
Более того, Spring Boot вводит аннотацию @SpringBootApplication
. Эта единственная аннотация эквивалентна использованию @Configuration
, @EnableAutoConfiguration
и @ComponentScan
.
Давайте используем эту аннотацию в основном классе приложения:
@SpringBootApplication
class VehicleFactoryApplication {
public static void main(String[] args) {
SpringApplication.run(VehicleFactoryApplication.class, args);
}
}
В результате, когда мы запускаем это приложение Spring Boot, оно автоматически сканирует компоненты в текущем пакете и его подпакетах . Таким образом, он зарегистрирует их в контексте приложения Spring и позволит нам внедрять bean-компоненты с помощью @Autowired
.
3. Использование @Autowired
После включения внедрения аннотаций мы можем использовать автосвязывание свойств, сеттеров и конструкторов .
3.1. @Autowired
в свойствах
Давайте посмотрим, как мы можем аннотировать свойство с помощью @Autowired
. Это устраняет необходимость в геттерах и сеттерах.
Во-первых, давайте определим bean-компонент fooFormatter
:
@Component("fooFormatter")
public class FooFormatter {
public String format() {
return "foo";
}
}
Затем мы внедрим этот bean-компонент в bean- компонент FooService
, используя @Autowired
в определении поля:
@Component
public class FooService {
@Autowired
private FooFormatter fooFormatter;
}
В результате Spring внедряет fooFormatter
при создании FooService
.
3.2. @Autowired
на сеттерах
Теперь давайте попробуем добавить аннотацию @Autowired
к методу установки.
В следующем примере метод установки вызывается с экземпляром FooFormatter
при создании FooService
:
public class FooService {
private FooFormatter fooFormatter;
@Autowired
public void setFooFormatter(FooFormatter fooFormatter) {
this.fooFormatter = fooFormatter;
}
}
3.3. @Autowired
для конструкторов
Наконец, давайте используем @Autowired
в конструкторе.
Мы увидим, что экземпляр FooFormatter
вводится Spring в качестве аргумента конструктору FooService
:
public class FooService {
private FooFormatter fooFormatter;
@Autowired
public FooService(FooFormatter fooFormatter) {
this.fooFormatter = fooFormatter;
}
}
4. @Autowired
и необязательные зависимости
При создании bean-компонента должны быть доступны зависимости @Autowired .
В противном случае, если Spring не может разрешить bean-компонент для связывания, он выдаст исключение .
Следовательно, он предотвращает успешный запуск контейнера Spring, за исключением формы:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type [com.autowire.sample.FooDAO] found for dependency:
expected at least 1 bean which qualifies as autowire candidate for this dependency.
Dependency annotations:
{@org.springframework.beans.factory.annotation.Autowired(required=true)}
Чтобы исправить это, нам нужно объявить bean-компонент требуемого типа:
public class FooService {
@Autowired(required = false)
private FooDAO dataAccessor;
}
5. Автопроводная неоднозначность
По умолчанию Spring разрешает записи @Autowired
по типу. Если в контейнере доступно более одного bean-компонента одного типа, фреймворк выдаст фатальное исключение .
Чтобы разрешить этот конфликт, нам нужно явно указать Spring, какой компонент мы хотим внедрить.
5.1. Автопроводка @Qualifier
Например, давайте посмотрим, как мы можем использовать аннотацию @Qualifier
для указания требуемого компонента.
Во-первых, мы определим 2 bean-компонента типа Formatter
:
@Component("fooFormatter")
public class FooFormatter implements Formatter {
public String format() {
return "foo";
}
}
@Component("barFormatter")
public class BarFormatter implements Formatter {
public String format() {
return "bar";
}
}
Теперь давайте попробуем внедрить bean-компонент Formatter в класс
FooService
:
public class FooService {
@Autowired
private Formatter formatter;
}
В нашем примере для контейнера Spring доступны две конкретные реализации Formatter .
В результате Spring выдаст исключение NoUniqueBeanDefinitionException
при построении FooService
: ``
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException:
No qualifying bean of type [com.autowire.sample.Formatter] is defined:
expected single matching bean but found 2: barFormatter,fooFormatter
Мы можем избежать этого, сузив реализацию с помощью аннотации @Qualifier
:
public class FooService {
@Autowired
@Qualifier("fooFormatter")
private Formatter formatter;
}
При наличии нескольких bean-компонентов одного типа рекомендуется использовать @Qualifier
, чтобы избежать двусмысленности.
Обратите внимание, что значение аннотации @Qualifier
совпадает с именем, объявленным в аннотации @Component нашей реализации
FooFormatter
.
5.2. Автопроводка по пользовательскому классификатору
Spring также позволяет нам создавать собственные аннотации @Qualifier
. Для этого мы должны предоставить аннотацию @Qualifier
с определением:
@Qualifier
@Target({
ElementType.FIELD, ElementType.METHOD, ElementType.TYPE, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface FormatterType {
String value();
}
Затем мы можем использовать FormatterType
в различных реализациях, чтобы указать пользовательское значение:
@FormatterType("Foo")
@Component
public class FooFormatter implements Formatter {
public String format() {
return "foo";
}
}
@FormatterType("Bar")
@Component
public class BarFormatter implements Formatter {
public String format() {
return "bar";
}
}
Наконец, наша пользовательская аннотация квалификатора готова к использованию для автоматического связывания:
@Component
public class FooService {
@Autowired
@FormatterType("Foo")
private Formatter formatter;
}
Значение, указанное в мета-аннотации @Target
, ограничивает применение квалификатора, которым в нашем примере являются поля, методы, типы и параметры.
5.3. Автопроводка по имени
Spring использует имя компонента в качестве значения квалификатора по умолчанию. Он будет проверять контейнер и искать bean-компонент с точным именем в качестве свойства для его автоматического связывания.
Следовательно, в нашем примере Spring сопоставляет имя свойства fooFormatter с реализацией
FooFormatter
. Поэтому он внедряет эту конкретную реализацию при создании FooService
:
public class FooService {
@Autowired
private Formatter fooFormatter;
}
6. Заключение
В этой статье мы обсудили автопроводку и различные способы ее использования. Мы также рассмотрели способы решения двух распространенных исключений автосвязывания, вызванных либо отсутствующим bean-компонентом, либо неоднозначной инъекцией bean-компонента.
Исходный код этой статьи доступен на проекте GitHub .