Что такое IoC и DI в Spring? Опишите жизненный цикл бина и основные скоупы.
Короткий ответ
- IoC — контейнер сам создаёт объекты и управляет их жизнью
- DI — зависимости внедряются извне: конструктор, сеттер, поле
- Конструкторное внедрение — рекомендуемое: иммутабельность и явные зависимости
- Цикл: создание → внедрение → BeanPostProcessor → @PostConstruct → работа → @PreDestroy
- Скоупы: singleton (по умолчанию), prototype, request, session
- Прокси и AOP навешиваются на этапе пост-процессоров
Spring-контейнер создаёт бины, внедряет зависимости и проводит их через управляемый жизненный цикл с точками расширения.
Как сказать вслух
пример ответаInversion of Control означает, что не я создаю объекты через new, а контейнер: он читает конфигурацию, строит бины и соединяет их между собой. Dependency Injection — способ передать зависимости, лучше всего через конструктор. Жизненный цикл такой: бин создаётся, в него внедряются зависимости, отрабатывают пост-процессоры — именно там появляются прокси для транзакций — потом метод инициализации, и при остановке контекста метод destroy. По умолчанию каждый бин — синглтон на контекст.
Подробный ответ
Основной ответ
IoC-контейнер (ApplicationContext) по конфигурации (@Component-сканирование, @Bean-методы) строит BeanDefinition, затем создаёт бины: инстанцирование → внедрение зависимостей → Aware-интерфейсы → BeanPostProcessor.postProcessBeforeInitialization → @PostConstruct/afterPropertiesSet → postProcessAfterInitialization (здесь оборачивание в AOP-прокси для @Transactional, @Async) → бин готов. При закрытии контекста — @PreDestroy. Скоупы: singleton — один экземпляр на контекст (по умолчанию), prototype — новый на каждый запрос к контейнеру (контейнер не управляет его уничтожением), web-скоупы request/session. DI через конструктор предпочтительнее полевого @Autowired: зависимости видны, можно сделать поля final, легко тестировать без Spring. Циклические зависимости конструкторов контейнер разрешить не может — это сигнал к рефакторингу.
Ключевые моменты
- BeanPostProcessor. Точка расширения, где рождаются прокси; поэтому self-invocation мимо прокси ломает @Transactional.
- Конструкторная инъекция. final-поля, явный контракт, простое юнит-тестирование; полевая инъекция затрудняет тесты.
- Скоупы. singleton — состояние шарится между потоками, поэтому бины должны быть stateless; prototype Spring не уничтожает.
- Инициализация. @PostConstruct — после внедрения всех зависимостей; конструктор для этого не годится.
Практический контекст
Это основа любого Java-бэкенд-интервью со Spring. Практические следствия: нельзя хранить изменяемое состояние в синглтоне, тяжёлую инициализацию переносить в @PostConstruct, а циклические зависимости лечить выделением третьего бина. Интервьюер любит связку вопросов: «почему конструкторная инъекция», «когда создаётся прокси», «что будет с prototype внутри singleton».
Частые ошибки
- Путают IoC (принцип) и DI (механизм его реализации)
- Хранят состояние запроса в singleton-бине и получают гонки
- Ожидают @PreDestroy у prototype-бинов — контейнер их не уничтожает