1. Обзор
Начиная с Spring 2.5, фреймворк представил внедрение зависимостей на основе аннотаций . Основная аннотация этой функции - @Autowired . Это позволяет Spring разрешать и внедрять сотрудничающие bean-компоненты в наш bean-компонент.
В этом руководстве мы сначала рассмотрим, как включить автоматическое подключение иразличныйспособы autowire beans. Позже мы поговорим о разрешении конфликтов bean-компонентов с помощью аннотации @Qualifier , а также о возможных сценариях исключения.
2. Включение аннотаций @Autowired
Фреймворк Spring обеспечивает автоматическое внедрение зависимостей. Другими словами, объявляя все зависимости bean-компонентов в файле конфигурации Spring, контейнер Spring может автоматически устанавливать связи между сотрудничающими bean-компонентами . Это называется автоматическим подключением компонентов Spring .
Чтобы использовать конфигурацию на основе Java в нашем приложении, давайте включим инъекцию на основе аннотацийчтобы загрузить нашу конфигурацию Spring:
@Configuration @ComponentScan("com.baeldung.autowire.sample") public class AppConfig {}
В качестве альтернативы Аннотации в основном используются для активации аннотаций внедрения зависимостей в 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 не может разрешить компонент для подключения, он выдаст исключение .
Следовательно, он препятствует успешному запуску контейнера 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. Устранение неоднозначности Autowire
По умолчанию Spring разрешает записи @Autowired по типу. Если в контейнере доступно более одного bean-компонента одного типа, инфраструктура выдаст фатальное исключение .
Чтобы разрешить этот конфликт, нам нужно явно указать Spring, какой bean-компонент мы хотим внедрить.
5.1. Автоподключение от @Qualifier
Например, давайте посмотрим, как мы можем использовать аннотацию @Qualifier для указания необходимого bean-компонента.
Сначала мы определим 2 компонента типа 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"; } }
Теперь давайте попробуем придать 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"; } }
Наконец, наша специальная аннотация Qualifier готова к использованию для автоматического подключения:
@Component public class FooService { @Autowired @FormatterType("Foo") private Formatter formatter; }
Значение, указанное в метааннотации @Target, ограничивает место применения квалификатора, которым в нашем примере являются поля, методы, типы и параметры.
5.3. Автоподключение по имени
Spring использует имя bean-компонента в качестве значения квалификатора по умолчанию. Он будет проверять контейнер и искать bean-компонент с точным именем в качестве свойства для его автосоединения.
Следовательно, в нашем примере Spring сопоставляет имя свойства fooFormatter с реализацией FooFormatter . Следовательно, он внедряет эту конкретную реализацию при создании FooService :
public class FooService { @Autowired private Formatter fooFormatter; }
6. Заключение
В этой статье мы обсудили автоматическое подключение и различные способы его использования. Мы также изучили способы решения двух распространенных исключений автоподключения, вызванных либо отсутствующим компонентом, либо неоднозначной инъекцией компонента.
Исходный код этой статьи доступен в проекте GitHub.