Зачем нужен Spring Framework, если можно создавать объекты вручную?
Что проверяют
Понимаете ли вы, что внедрение зависимостей это паттерн, а Spring лишь один из способов его реализовать. Интервьюер ждёт не рекламы фреймворка, а честного разбора: что даёт контейнер сверх ручного new. Следом обычно спрашивают, как работает @Transactional и почему он не срабатывает при вызове метода изнутри того же класса.
Как отвечать
Начните с того, что создавать объекты вручную можно, если граф маленький: собрать всё в main через конструкторы это нормальная техника. Проблемы начинаются, когда объектов сотни. Контейнер Spring (ApplicationContext) берёт на себя несколько вещей:
- сборку графа: вы объявляете бины через
@Componentили@Beanв@Configuration, а контейнер сам находит зависимости и порядок создания. Рекомендуемый способ внедрения через конструктор; - жизненный цикл и области видимости: по умолчанию
singleton, естьprototype,request,session, хуки@PostConstructи@PreDestroy; - прокси:
@Transactional,@Cacheable,@Asyncи проверки Spring Security работают через обёртку вокруг бина. Объект, созданный черезnew, этого поведения не получит; - конфигурацию:
application.yml, профили, переменные окружения, привязка через@ConfigurationProperties; - экосистему: Spring Boot с автоконфигурацией, Spring Data, Security, интеграционные тесты через
@SpringBootTestс подменой бинов на моки.
Хорошо упомянуть альтернативы: Dagger с генерацией кода на этапе компиляции, Guice, Micronaut и Quarkus, которые строят граф при сборке и стартуют быстрее. Это показывает, что вы выбираете инструмент, а не просто привыкли к нему.
Где спотыкаются
Говорят, что без Spring внедрение зависимостей невозможно. Конструктор с параметрами это уже DI, контейнер лишь автоматизирует сборку.
Вызывают @Transactional-метод из другого метода того же бина и ждут транзакцию. Вызов идёт мимо прокси, и аннотация не работает.
Внедряют зависимости в поле через @Autowired. Такой класс сложно создать в юнит-тесте без контейнера, зависимости не видны в конструкторе и не могут быть final.