Почему предпочтительнее использовать Dependency Injection вместо создания объектов вручную
Что проверяют
Понимаете ли вы, что DI это про распределение ответственности за создание объектов, а не про аннотации Spring. Второй слой вопроса: сможете ли вы объяснить пользу через тесты и границы, а не общими словами про связанность.
Как отвечать
Класс, который сам вызывает new для своей зависимости, зашивает в себя конкретную реализацию и её настройку. Инъекция переворачивает ответственность: класс объявляет, что ему нужно, а собирает граф кто-то снаружи. Отсюда всё остальное:
- тест создаёт объект сам и подставляет заглушку в конструктор, без контейнера и без подмены статических вызовов;
- реализация меняется в одном месте сборки, а не правкой каждого потребителя;
- жизненный цикл и настройка (пул соединений, клиент HTTP, транзакции) задаются однажды, а не в каждом классе.
Про Spring скажите, что предпочитаете инъекцию через конструктор: поля становятся final, объект невозможно создать недособранным, а разросшийся список параметров честно показывает, что класс взял на себя лишнее. Инъекция в поле прячет зависимости и тянет контейнер в тест. Единственному конструктору аннотация не нужна.
Полезно добавить, что DI это паттерн, а не фреймворк: собрать граф руками в точке входа тоже DI, и в небольшом сервисе это разумный выбор.
Где спотыкаются
Говорят, что DI избавляет от циклических зависимостей. Наоборот, он их проявляет: цикл в конструкторах виден на старте приложения и означает, что обязанности между классами разделены неверно.
Путают DI с сервис-локатором. Класс, который сам достаёт зависимость из контекста, снова связан с контейнером, а его зависимости не видны в сигнатуре, то есть теряется ровно то, ради чего всё затевалось.
Не называют цену. Ошибки сборки графа вылезают при старте, а не при компиляции, а вызов метода того же объекта через this идёт мимо прокси, поэтому @Transactional и кэширование внутри класса молча не работают.