DEV Community

faangmaster
faangmaster

Posted on

Что такое @Autowire в Spring? Как его использовать и как он работает?

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

Использование @Autowired

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

Смотрите документацию тут: Using @Autowired

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

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

@Component("myDependency")
public class MyDependency {
    ......
}
Enter fullscreen mode Exit fullscreen mode

Также можно автовайрить массив бинов одного типа:

public class MovieRecommender {
    @Autowired
    private MovieCatalog[] movieCatalogs;
}
Enter fullscreen mode Exit fullscreen mode

Т.е. если у нас в ApplicationContext много бинов одного и того же типа MovieCatalog, все они будут заинжекчены в массив movieCatalogs.
Автовайринг также работает для конструктора класса:

public class MovieRecommender {
    private final CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
        this.customerPreferenceDao = customerPreferenceDao;
    }
....
}
Enter fullscreen mode Exit fullscreen mode

И для сеттеров:

public class SimpleMovieLister {
    private MovieFinder movieFinder;

    @Autowired
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
....
}
Enter fullscreen mode Exit fullscreen mode

Начиная с версии Spring Framework 4.3, аннотация @Autowired для конструктора больше не требуется, если в бине только один конструктор. Однако, если в нем несколько конструкторов, то хотя бы один из конструкторов должен быть аннотирован @Autowired, чтобы указать контейнеру, какой из них использовать.

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

public class FooService {
    @Autowired(required = false)
    private FooDAO dataAccessor; 
}
Enter fullscreen mode Exit fullscreen mode

В таком случае бин FooService будет создан даже если бин FooDAO не может быть зарезолвен.
Или можно использовать Optional:

public class SimpleMovieLister {
    @Autowired
    public void setMovieFinder(Optional<MovieFinder> movieFinder)
    {
    ....
    }
}
Enter fullscreen mode Exit fullscreen mode

Начиная с версии Spring 5.0 можно использовать @Nullable:

public class SimpleMovieLister {

    @Autowired
    public void setMovieFinder(@Nullable MovieFinder movieFinder) {
        ...
    }
}
Enter fullscreen mode Exit fullscreen mode

Можно автовайрить бины одного типа в коллекцию:

public class MovieRecommender {

    private Set<MovieCatalog> movieCatalogs;

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

    // ...
}
Enter fullscreen mode Exit fullscreen mode

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

public class MovieRecommender {

    private Map<String, MovieCatalog> movieCatalogs;

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

    // ...
}
Enter fullscreen mode Exit fullscreen mode

Резолвинг зависимостей

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

Например:

@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;
}
Enter fullscreen mode Exit fullscreen mode

В данном случае будет брошено исключение NoUniqueBeanDefinitionException, т.к. у нас два бина одного и того же и типа и при попытке заинжектить зависимость при помощи автовайринга, Spring не сможет зарезолвить какой из бинов инжектить.
Для решения этой проблемы можно использовать @Qualifier для указания, какой именно бин нам нужен:

public class FooService {
    @Autowired
    @Qualifier("fooFormatter")
    private Formatter formatter;
}
Enter fullscreen mode Exit fullscreen mode

Если @Qualifier не указывать, то Spring попытается заметчить имя поля с именем бина:

public class FooService {
    @Autowired 
    private Formatter fooFormatter; 
}
Enter fullscreen mode Exit fullscreen mode

Как включить Автовайринг?

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

@SpringBootApplication
public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }
}
Enter fullscreen mode Exit fullscreen mode

В результате Spring будет сканировать бины в рамках текущего package и всех его sub-packages. Регистрировать их в Application Context и инжектить бины, используя автовайринг.

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

@Configuration
@ComponentScan("com.mypackage")
public class AppConfig {}
Enter fullscreen mode Exit fullscreen mode

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

<context:annotation-config/>
Enter fullscreen mode Exit fullscreen mode

в наш XML-конфиг.

Top comments (0)