Варианты DI в Spring | OTUS
⚡ Подписка на курсы OTUS!
Интенсивная прокачка навыков для IT-специалистов!
Подробнее

Курсы

Программирование
C++ Developer. Professional JavaScript Developer. Professional Android Developer. Professional Microservice Architecture React.js Developer JavaScript Developer. Basic PostgreSQL Программист С C++ Developer. Basic Team Lead PHP Developer. Professional Подготовка к сертификации Oracle Java Programmer (OCAJP) Алгоритмы и структуры данных Разработчик IoT C# Developer. Basic Unreal Engine Technical Game Design C# ASP.NET Core разработчик Python Developer. Professional Python Developer. Basic Node.js Developer iOS Developer. Professional Cloud Solution Architecture Kotlin Backend Developer Agile Project Manager Scala-разработчик Symfony Framework iOS Developer. Basic Супер-интенсив Azure Специализация Python Developer
Инфраструктура
Экспресс-курс по управлению миграциями (DBVC) Экспресс-курс «IaC Ansible» Microservice Architecture Разработчик программных роботов (RPA) на базе UiPath и PIX Внедрение и работа в DevSecOps NoSQL Специализация Administrator Linux
-24%
Разработчик IoT Мониторинг и логирование: Zabbix, Prometheus, ELK MongoDB
-30%
DevOps практики и инструменты MS SQL Server Developer SRE практики и инструменты Administrator Linux. Advanced Infrastructure as a code Супер-интенсив "Tarantool" Специализация Network engineer
Корпоративные курсы
Экспресс-курс по управлению миграциями (DBVC) Экспресс-курс «IaC Ansible» Разработчик программных роботов (RPA) на базе UiPath и PIX Внедрение и работа в DevSecOps NoSQL Spark Developer Экспресс-курс «CI/CD или Непрерывная поставка с Docker и Kubernetes» Game QA Engineer IT-Recruiter Enterprise Architect Node.js Developer Cloud Solution Architecture Agile Project Manager Супер-практикум по работе с протоколом BGP Infrastructure as a code Промышленный ML на больших данных Супер-интенсив Azure Руководитель поддержки пользователей в IT
Специализации Курсы в разработке Подготовительные курсы Подписка
+7 499 938-92-02

Варианты DI в Spring

Dependency Injection в Spring можно осуществлять различными способами. Рассмотрим плюсы и минусы данных подходов.

Например, рассмотрим DI через параметры конструктора

@Service
public class MyService {
    private Dependency dependency;
    @Autowired
    public MyService(Dependency dependency) {
        this.dependency = dependency;
    }
}
Данный подход является рекомендованным. Разберёмся с некоторыми ситуациями, где DI через параметры конструктора может выглядеть не самым лучшим образом, и выясним, причины этого:

Чуешь, чем пахнет?
Появляется большое число параметров конструктора, что является «запахом плохого кода». Если для создания бина требуется множество зависимостей, то DI через конструктор, как ни странно, ни при чём. Если в классе большое число зависимостей, то это говорит, скорее о неправильном дизайне самого сервиса.

Как пони по кругу
Если между классами есть циклическая зависимость, то DI через конструктор не получится организовать. Действительно, это так, но если появилась циклическая зависимость между классами, то это, скорее, неправильное разделение ответственности между сервисами.

У DI через параметры конструктора, есть безусловные плюсы:
  • в любой момент времени вы всегда получите готовый к работе класс со всеми зависимостями;
  • данные классы можно просто тестировать как обычные Java-классы и без поднятия контекста для тестирования;
  • в unit-тестах вы не сможете забыть добавить какую-либо зависимость, так как она будет требоваться на уровне компиляции.

  • С циклическими зависимостями можно, в частности, бороться с помощью

    DI через поля:

    @Service
    public class MyService {
        @Autowired
        private Dependency dependency;
    }
    
    Несмотря на действительно минимальное количество кода, этот подход обладает одним большим недостатком. Данный класс нельзя просто протестировать, не поднимая Spring Context. Если для контроллеров и других классов в любом случае придётся его поднимать, то для бизнес-сервисов это излишне.

    Ну и DI через сеттеры

    @Service
    public class MyService {
        private Dependency dependency;
        @Autowired
        public void setDependency(Dpendency dependency) {
            this.dependency = dependency;
        }
    }
    
    В данном случае тестировать эти классы почти так же просто, как и в случае DI через параметры конструктора. Но в тестах очень легко забыть проставить зависимость, не вызвав свежедобавленный сеттер.

    Этот способ обладает одним серьёзным недостатком: до того момента, как Spring проставит все зависимости в бин, экземпляр класса находится в некорректном состоянии. И обращение к зависимостям может привести к NullPointerException. Для того, чтобы сделать какие-то действия после того, как все зависимости будут инъектированы, используют аннотацию @PostConstruct:

    @Service
    public class MyService {
        private Dependency dependency;

    @Autowired public void setDependency(Dpendency dependency) { this.dependency = dependency; }

    @PostConstruct public void init() { this.dependency.doSomething(); } }

    Остались вопросы? Задавайте в комментариях!

    Не пропустите новые полезные статьи!

    Спасибо за подписку!

    Мы отправили вам письмо для подтверждения вашего email.
    С уважением, OTUS!

    Автор
    0 комментариев
    Для комментирования необходимо авторизоваться