Как сказал один мудрец: вечно смотреть можно на три вещи: как течет вода, как горит огонь и как кто-то пишет очередную стать о 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 как поле класса
1 2 3 4 5 6 7 8 9 10 11 |
@Controller public class PropertyInjectedController { @Autowired public GreetingServiceImpl greetingService; public String sayHello(){ return greetingService.sayGreeting(); } } |
Недостатки:
- Публичное поле
- Нельзя указать интерфейс, только конкретную реализацию. Использовать DI интерфейсов это хорошая практика, дающая много преимуществ и открывающая перед вами другие инструменты Spring Boot для внедрения зависимостей, но об этом как-нибудь в другой раз.
DI как сеттер
1 2 3 4 5 6 7 8 9 10 11 12 13 |
@Controller public class SetterInjectedController { private GreetingService greetingService; public String sayHello(){ return greetingService.sayGreeting(); } @Autowired public void setGreetingService(GreetingService greetingService) { this.greetingService = greetingService; } } |
Главный недостаток описан выше.
DI как конструктор
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
@Controller public class ConstructorInjectedController { private GreetingService greetingService; @Autowired public ConstructorInjectedController(GreetingService greetingService) { this.greetingService = greetingService; } public String sayHello(){ return greetingService.sayGreeting(); } } |
Какой из способов использовать – решать вам. Главное, чтобы это был DI с помощью конструктора.