Урок 01 из 18 · 2 минуты чтения

IoC и Dependency Injection

Принцип инверсии управления, DI как его форма, три способа внедрения и почему конструктор, разрешение циклических зависимостей.

Inversion of Control

Inversion of Control (инверсия управления) — принцип: контроль над созданием, конфигурацией и жизненным циклом объектов передаётся внешнему контейнеру, а не пишется руками в коде. Это «Hollywood principle»: не вы зовёте фреймворк, а он зовёт ваш код.

В Spring роль контейнера играет ApplicationContext: он создаёт объекты-бины, связывает их зависимости и управляет их жизнью. Ваш код перестаёт делать new для своих зависимостей.

Dependency Injection

DI — конкретная форма IoC: зависимости объекта передаются ему извне (контейнером), а не создаются им самим внутри через new. Объект лишь объявляет, что ему нужно, а кто и как это предоставит — забота контейнера.

Важно не путать понятия: IoC — широкий принцип (управление отдано наружу), DI — один из способов его реализовать (через внедрение зависимостей). На собеседовании это частый уточняющий вопрос.

Три способа внедрения

Constructor injection — зависимости приходят параметрами конструктора (рекомендуемый). Setter injection — через сеттеры (для опциональных или переустанавливаемых зависимостей). Field injection — прямо в поле через @Autowired (не рекомендуется).

С единственным конструктором аннотация @Autowired не нужна — Spring использует его автоматически.

@Service
class OrderService {
    private final PaymentGateway gateway; // final — гарантированно установлен
    OrderService(PaymentGateway gateway) { // один конструктор — @Autowired не нужен
        this.gateway = gateway;
    }
}

Почему конструктор

Конструкторная инъекция объективно лучше, а не просто «стиль». Поля можно сделать final (иммутабельность и безопасная публикация). Объект всегда создаётся полностью собранным — не бывает промежуточного состояния с null-зависимостями. Обязательные зависимости становятся явными: класс невозможно создать без них (fail-fast уже на старте контекста, а не NPE в рантайме).

И главное для качества кода — тестируемость: класс с конструкторной инъекцией тестируется обычным new с моками, без поднятия Spring-контекста. Field injection, наоборот, прячет зависимости (по сигнатуре не видно, что нужно классу) и требует рефлексии/контейнера в тестах.

Циклические зависимости

Если A зависит от B, а B — от A через конструкторы, контейнер не может их собрать (для создания A нужен готовый B и наоборот) и бросает BeanCurrentlyInCreationException на старте.

Цикл «лечат» setter/field-инъекцией или @Lazy/ObjectProvider, которые откладывают получение зависимости. Но это симптом: чаще всего циклическая зависимость — признак плохого дизайна, и правильный путь — рефакторинг (выделить третий компонент с общей логикой), а не обход. С Spring Boot 2.6+ циклы по умолчанию запрещены, что подталкивает именно к рефакторингу.

Как отвечать на собеседовании

Чётко разделите IoC (принцип: управление отдано контейнеру) и DI (форма IoC: внедрение зависимостей). Перечислите три вида инъекции и аргументируйте конструктор по существу (final, полностью собранный объект, явные обязательные зависимости, fail-fast, тестируемость без контекста). Объясните, почему конструкторный цикл неразрешим и что это повод к рефакторингу, а не к обходу.

Проверка урока

Войдите, чтобы проверить себя: сданные проверки всех уроков курса отмечают его темы пройденными на карте и в ИПР.

Вопросы по теме

Читайте также

Партнёр платформы

Разбор собеседования с sotto

После собеседования sotto разбирает разговор: оценка, каждый вопрос — хорошо, частично или слабо, чего не хватило в ответе и над чем поработать к следующему разу. Первые 30 минут бесплатно.

Разбор собеседования