Как сказал один мудрец: вечно смотреть можно на три вещи: как течет вода, как горит огонь и как кто-то пишет очередную стать о DI в Spring Boot. Сегодня я сделаю попытку коротко описать всю эту петрушку.
DI (Dependency Injection, внедрение зависимости) — процесс при котором построение одного объекта, предоставляется внешнему объекту. Или точнее это то место, где зависимость будет внедрена другим объектом. Понятнее будет на примере. Пусть у нас есть класс «Автомобиль» у него есть поле класса «Двигатель». Место где «Двигатель» будет инициализирован в «Автомобиле» и будет внедрением зависимости.
В Spring Boot существует 4 типа DI:
- Как поле класса
- Как приватное поле класса (прочитали что так можно делать DI, теперь забудьте — способ 2 ЗЛО!)
- С помощью setter
- С помощью конструктора
DI с помощью «сеттера» до сих пор остается местом споров. Это связано с другой особенностью Spring Boot — Ioc (Inversion of Controll, инверсия контроля). IoC — передача управления. Обычно программист сам решает когда вызывать ту или иную процедуру, делать DI и т.п. В Spring Boot — это делает IoC — инициализация и вызовы процедур в Runtime. Получается, что используя DI с помощью «сеттера» вы не можете знать в какой именно момент вы зависимость будет внедрена.
DI как поле класса используется редко по причине нарушения инкапсуляции, ведь внедряемое поле должно быть помечено как public.
DI с помощью конструктора — наиболее предпочтительный способ осуществления DI. Связывание будет осуществлено в момент создания объекта и вы точно будете знать когда именно IoC осуществит вызов.
В Spring Boot в месте, где будет осуществлена инъекция необходимо ставить аннотацию @Autowired (автосвязывание). Однако, если вы используете DI с помощью конструктора использовать эту аннотацию необязательно.
Ниже я приведу пример каждого способа DI кроме 2, ведь, мы на светлой стороне силы, так?
DI как поле класса
@Controller
public class PropertyInjectedController {
@Autowired
public GreetingServiceImpl greetingService;
public String sayHello(){
return greetingService.sayGreeting();
}
}
Недостатки:
- Публичное поле
- Нельзя указать интерфейс, только конкретную реализацию. Использовать DI интерфейсов это хорошая практика, дающая много преимуществ и открывающая перед вами другие инструменты Spring Boot для внедрения зависимостей, но об этом как-нибудь в другой раз.
DI как сеттер
@Controller
public class SetterInjectedController {
private GreetingService greetingService;
public String sayHello(){
return greetingService.sayGreeting();
}
@Autowired
public void setGreetingService(GreetingService greetingService) {
this.greetingService = greetingService;
}
}
Главный недостаток описан выше.
DI как конструктор
@Controller
public class ConstructorInjectedController {
private GreetingService greetingService;
@Autowired
public ConstructorInjectedController(GreetingService greetingService) {
this.greetingService = greetingService;
}
public String sayHello(){
return greetingService.sayGreeting();
}
}
Какой из способов использовать — решать вам. Главное, чтобы это был DI с помощью конструктора.
Автор: мистер Олимпия