Ответ
Spring Boot реализует Dependency Injection (DI) через механизм Inversion of Control (IoC) контейнера, который автоматически управляет жизненным циклом объектов (бинов) и их зависимостями.
Основные механизмы:
-
Объявление бинов:
- Стереотипные аннотации:
@Component,@Service,@Repository,@Controller. Spring сканирует класспат и регистрирует помеченные классы как бины. - Явное объявление: В классах, помеченных
@Configuration, методы с@Beanвозвращают экземпляр бина.
- Стереотипные аннотации:
-
Внедрение зависимостей:
-
Через конструктор (рекомендуется): Наиболее предпочтительный способ, обеспечивает неизменяемость и обязательность зависимости.
@Service public class UserService { private final UserRepository userRepo; @Autowired // Необязательно с Spring 4.3+ public UserService(UserRepository userRepo) { this.userRepo = userRepo; } } - Через поле: Используется
@Autowiredнад полем. Менее тестируемо и скрывает обязательные зависимости. - Через сеттер: Аналогично полю, но с аннотацией над setter-методом.
-
Как это работает в Spring Boot:
- Аннотация
@SpringBootApplicationвключает автоматическое сканирование компонентов и автоконфигурацию. - Контейнер IoC создает граф зависимостей, разрешая их на этапе запуска приложения.
- Для устранения неоднозначности (несколько бинов одного типа) используются
@Qualifierили@Primary.
Ответ 18+ 🔞
А, слушай, вот эта ваша магия Spring Boot, где всё само подключается! Ну, типа, не совсем само, конечно, но почти. Смотри, как это работает, если на пальцах.
Представь себе бар. Ты — бармен (это твоё приложение). Тебе для коктейля нужны: водка, тоник, лёд. Это твои зависимости (dependencies). Раньше ты сам бегал на склад, искал эти бутылки, ковырялся в морозилке — это был ручной контроль. А теперь приходит Spring Boot, такой зазывала, и говорит: «Расслабься, чувак, я тебе всё сам принесу и даже в шейкер положу. Ты только скажи, что тебе надо». Это и есть Inversion of Control (IoC) — контроль перевернулся, теперь не ты за всем бегаешь, а за тобой.
А сам процесс, когда этот зазывала тебе в руки суёт нужную бутылку, — это и есть Dependency Injection (DI), внедрение зависимости. Не ты её берёшь, а тебе её внедряют прямо в руки, в рот меня чих-пых!
Как он узнаёт, что тебе принести? Есть два основных способа:
-
Кричишь громко (Стереотипные аннотации). Ты просто вешаешь на складскую дверь табличку: «Здесь Водка» (
@Component), «Здесь Тоник» (@Service), «Здесь Лёд» (@Repository). Spring Boot, как сканер штрих-кодов, бегает по всему твоему проекту (класс-пасу), видит эти таблички и говорит: «Ага, вот это бин! Беру в список». Бин — это просто объект, которым он будет управлять. -
Даёшь письменную инструкцию (Явное объявление). Бывает, компонент такой хитрожопый, что табличкой не обойдёшься. Например, тебе нужна не просто водка, а водка, настоянная на перце. Тогда ты идешь в отдельный конфигурационный кабинет (класс с
@Configuration) и пишешь метод с рецептом, помечая его@Bean. Spring Boot выполнит этот метод, получит твою перцовку и тоже запишет её как бин.
А как он эти бины тебе впихивает? Тоже есть варианты, но есть один, который все умные дяди рекомендуют:
-
Через конструктор (Идеально!). Ты заранее объявляешь: «Слушай, Spring, я бармен
UserService, но я нихрена не работаю безUserRepository! Не принесёшь — я даже создаваться не буду». И прописываешь это в конструкторе.@Service public class UserService { private final UserRepository userRepo; // Финал! Менять нельзя! // Spring видит конструктор и понимает: "О, чуваку нужен репозиторий. Ща найдём и сунем." public UserService(UserRepository userRepo) { this.userRepo = userRepo; // Всё, зависимость внедрена. Красиво и надёжно. } }Раньше надо было ставить над конструктором
@Autowired, но теперь Spring такой догадливый, что если конструктор один, то он всё понимает сам. Умный, блядь, как мартышлюшка! -
Через поле (Быстро, но криво). Ты просто объявляешь поле и ставишь над ним
@Autowired. Spring найдёт бин и впендюрит его туда напрямую, через отражение. Минус — хрен протестируешь, и не видно, что зависимость обязательная. Выглядит как чёрная магия, а магия, она, случается, и подводит. -
Через сеттер (Ну такое...). Почти как поле, но аннотацию вешаешь на метод set. Чуть лучше, но всё равно не айс.
И как же вся эта ёбаная кухня заводится в Spring Boot?
Всё начинается с аннотации @SpringBootApplication. Это такая красная кнопка «ВКЛ». Она включает:
- Сканирование компонентов: Spring начинает рыскать по пакетам и искать твои таблички (
@Component,@Serviceи т.д.). - Автоконфигурацию: Spring Boot сам догадывается, что у тебя, например, есть библиотека для работы с БД, и подсовывает тебе готовые настройки подключения. Не надо писать хуеву тучу конфигов!
Дальше контейнер IoC строит из всех найденных бинов граф: кто от кого зависит. И на этапе запуска приложения он этот граф собирает, как конструктор «Лего». Если всё нашёл — приложение стартует. Если нет — получаешь ошибку, что UserService не может найти бин типа UserRepository. И тут начинается дебаг, волнение ебать, терпения ноль ебать!
А если бинов одного типа несколько? Представь, у тебя на складе три сорта водки: «Столичная», «Абсолют» и какая-то местная «Беленькая». Spring увидит три бина типа Vodka и обосрётся от неопределённости: «Какую тебе, мудак, нести?!». Тут на помощь приходят:
@Qualifier("stolichnaya")— ты прямо указываешь, какую именно.@Primary— ты на одну из бутылок вешаешь бирку «По умолчанию бери эту». И если явно не указано, Spring будет тыкать её.
Вот, короче, и вся магия. Не самолётостроение, но когда понимаешь, как оно внутри работает, перестаёшь бояться этих @Autowired и начинаешь их уважать. Ну, почти.