Что такое @Autowire в Spring? Как его использовать и как он работает?
Published: 2024-03-21 00:21:59

Начиная с версии 2.5 Spring поддерживает Dependency Injection при помощи аннотаций. Одной из ключевых, такого рода аннотаций, является @Autowired. Она позволяет резолвить и инжектить зависимости во время инициализации бина.

## Использование @Autowired
Автовайринг можно использовать на полях класса, сеттерах и конструкторах.

Смотрите документацию тут: [Using @Autowired](https://docs.spring.io/spring-framework/reference/core/beans/annotation-config/autowired.html)

Пример автовайринга полей класса:

```java
@Component
public class MyService {  
    @Autowired
    private MyDependency myDependency;
}

@Component("myDependency")
public class MyDependency {
    ......
}
```
Также можно автовайрить массив бинов одного типа:

```java
public class MovieRecommender {
    @Autowired
    private MovieCatalog[] movieCatalogs;
}
```
Т.е. если у нас в ApplicationContext много бинов одного и того же типа MovieCatalog, все они будут заинжекчены в массив movieCatalogs.
Автовайринг также работает для конструктора класса:

```java
public class MovieRecommender {
    private final CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
        this.customerPreferenceDao = customerPreferenceDao;
    }
....
}
```
И для сеттеров:

```java
public class SimpleMovieLister {
    private MovieFinder movieFinder;

    @Autowired
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
....
}
```
Начиная с версии Spring Framework 4.3, аннотация @Autowired для конструктора больше не требуется, если в бине только один конструктор. Однако, если в нем несколько конструкторов, то хотя бы один из конструкторов должен быть аннотирован @Autowired, чтобы указать контейнеру, какой из них использовать.

По умолчанию, если зависимость нельзя зарезолвить, то будет брошено исключение NoSuchBeanDefinitionException. Если мы все равно хотим создать бин, даже если не получается зарезолвить зависимость, то можно использовать required = false:

```java
public class FooService {
    @Autowired(required = false)
    private FooDAO dataAccessor; 
}
```
В таком случае бин FooService будет создан даже если бин FooDAO не может быть зарезолвен.
Или можно использовать Optional:

```java
public class SimpleMovieLister {
    @Autowired
    public void setMovieFinder(Optional<MovieFinder> movieFinder)
    {
    ....
    }
}
```
Начиная с версии Spring 5.0 можно использовать @Nullable:

```java
public class SimpleMovieLister {

	@Autowired
	public void setMovieFinder(@Nullable MovieFinder movieFinder) {
		...
	}
}
```
Можно автовайрить бины одного типа в коллекцию:

```java
public class MovieRecommender {

	private Set<MovieCatalog> movieCatalogs;

	@Autowired
	public void setMovieCatalogs(Set<MovieCatalog> movieCatalogs) {
		this.movieCatalogs = movieCatalogs;
	}

	// ...
}
```
Если нам нужно, чтобы бины инжектились в коллекцию в определенном порядке, то можно использовать на бинах аннотацию @Order или @Priority.
Также можно инжектить Map, у которого ключ это имя бина в виде строки, а значение - сам бин:

```java
public class MovieRecommender {

	private Map<String, MovieCatalog> movieCatalogs;

	@Autowired
	public void setMovieCatalogs(Map<String, MovieCatalog> movieCatalogs) {
		this.movieCatalogs = movieCatalogs;
	}

	// ...
}
```

## Резолвинг зависимостей
По умолчанию, Spring резолвит @Autowired зависимости **по типу**. Если у нас больше одного бина одного и того же типа и мы не инжектим коллекцию или Map, то может быть брошено исключение, если мы не зарезолвим конфликты при помощи @Qualifier или по имени.

Например:

```java
@Component("fooFormatter")
public class FooFormatter implements Formatter {
    public String format() {
        return "foo";
    }
}

@Component("barFormatter")
public class BarFormatter implements Formatter {
    public String format() {
        return "bar";
    }
}
public class FooService {
    @Autowired
    private Formatter formatter;
}
```
В данном случае будет брошено исключение NoUniqueBeanDefinitionException, т.к. у нас два бина одного и того же и типа и при попытке заинжектить зависимость при помощи автовайринга, Spring не сможет зарезолвить какой из бинов инжектить.
Для решения этой проблемы можно использовать @Qualifier для указания, какой именно бин нам нужен:

```java
public class FooService {
    @Autowired
    @Qualifier("fooFormatter")
    private Formatter formatter;
}
```
Если @Qualifier не указывать, то Spring попытается заметчить имя поля с именем бина:

```java
public class FooService {
    @Autowired 
    private Formatter fooFormatter; 
}
```

## Как включить Автовайринг?
Если вы используете Spring Boot, то @SpringBootApplication аннотация включает в себя @Configuration, @EnableAutoConfiguration, и @ComponentScan. Что достаточно, чтобы работал Автовайринг:

```java
@SpringBootApplication
public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }
}
```
В результате Spring будет сканировать бины в рамках текущего package и всех его sub-packages. Регистрировать их в Application Context и инжектить бины, используя автовайринг.

Если мы не используем Spring Boot, то можно включить автовайринг в Java конфигурации:

```java
@Configuration
@ComponentScan("com.mypackage")
public class AppConfig {}
```

Или если мы используем XML-конфиги, то нужно добавить

```
<context:annotation-config/>
```
в наш XML-конфиг.