Ответ
Внедрение зависимостей (Dependency Injection, DI) — это паттерн проектирования, при котором объект получает свои зависимости извне, а не создаёт их самостоятельно. В Spring это реализовано через IoC-контейнер, который управляет жизненным циклом бинов и их зависимостями.
Зачем это нужно:
- Слабая связанность: Классы не зависят от конкретных реализаций, а от абстракций (интерфейсов).
- Тестируемость: Зависимости легко подменить моками в unit-тестах.
- Гибкость: Конфигурацию зависимостей можно менять без изменения кода (например, через конфигурационные файлы или аннотации).
Основные способы внедрения в Spring:
-
Через конструктор (рекомендуется):
@Service public class OrderService { private final PaymentProcessor paymentProcessor; @Autowired // Начиная с Spring 4.3, для одного конструктора необязательна public OrderService(PaymentProcessor paymentProcessor) { this.paymentProcessor = paymentProcessor; } } - Через поле (field injection):
@Service public class OrderService { @Autowired private PaymentProcessor paymentProcessor; } -
Через сеттер (setter injection):
@Service public class OrderService { private PaymentProcessor paymentProcessor; @Autowired public void setPaymentProcessor(PaymentProcessor paymentProcessor) { this.paymentProcessor = paymentProcessor; } }
Ключевые аннотации: @Autowired, @Qualifier, @Primary, @Resource, @Inject. Контейнер Spring ищет подходящий бин и автоматически связывает его (wires) с целевым компонентом.
Ответ 18+ 🔞
А, слушай, смотри, сейчас объясню про эту всю хуйню с внедрением зависимостей в Spring, чтобы ты не сидел как Герасим, немой, и не мычал "Му-му" на каждый чих.
Короче, представь, ты пишешь какой-нибудь OrderService. И ему, этому сервису, нужен какой-нибудь PaymentProcessor, чтобы деньги брать. Так вот, самый тупой и прямолинейный способ — это внутри сервиса самому, своими руками, создать этот процессор: new SuperPaymentProcessor(). И вроде бы всё работает, да?
А теперь представь, что завтра тебе говорят: "Вася, а давай вместо супер-процессора поставим мега-процессор, он с комиссией 0.5%". И ты, блядь, начинаешь ползать по всему коду, искать все эти new SuperPaymentProcessor() и менять их на new MegaPaymentProcessor(). Это пиздец какой-то, а не жизнь. Терпения ноль, ебать.
Вот чтобы такого не было, умные дядьки придумали Dependency Injection (DI). Суть проста, как три рубля: ты не создаёшь зависимости сам, а говоришь: "Эй, Spring, мне нужен PaymentProcessor!". А он тебе, хитрая жопа, подсовывает готовый, настроенный бин. И если завтра надо поменять реализацию, ты меняешь её в одном месте — в конфигурации контейнера. И всё, блядь, автоматически подтянется. Ёперный театр, а не удобство.
Зачем это, спросишь? Да похуй, спросишь — не спросишь, я всё равно расскажу:
- Слабая связанность: Твой сервис теперь не привязан намертво к конкретному классу. Он просто знает, что ему дадут что-то, что умеет обрабатывать платежи (
PaymentProcessor— интерфейс). А что именно —SuperPaymentилиMegaPayment— ему, в общем-то, похуй. Главное, чтобы контракт соблюдал. - Тестируемость: Это вообще песня. Хочешь протестировать сервис? Подсовываешь ему в тесте не реальный процессор, который лезет в банк, а какую-нибудь заглушку-мок, которая всегда говорит "ОК". И не надо ебаться с сетями, SSL и прочей хуйней. Чистый юнит-тест.
- Гибкость: Конфигурация отдельно, код отдельно. Меняешь аннотацию или проперти-файл — и получаешь другое поведение. Красота, блядь.
А теперь, как этим пользоваться. Способов, блядь, целый овердохуища, но основных три:
-
Через конструктор (это сейчас все рекомендуют, и правильно делают): Ты просто объявляешь зависимость как
finalполе в классе и требуешь её в конструкторе. Spring видит: "Ага, у класса один конструктор, и там нуженPaymentProcessor. Так, у меня такой бин есть, хуяк — и подсовывает". Чисто, надёжно, все зависимости видны как на ладони.@Service public class OrderService { private final PaymentProcessor paymentProcessor; // final, блядь! Менять нельзя! // @Autowired тут можно не писать, Spring 4.3+ и так сообразит public OrderService(PaymentProcessor paymentProcessor) { this.paymentProcessor = paymentProcessor; // Всё, приехали, зависимость внедрена } } -
Через поле (field injection). Самый ленивый и опасный способ: Просто навешиваешь
@Autowiredнад полем. Spring через рефлексию (это такая магия, блядь) засовывает туда бин. Минусы? Да пиздец какие: поле нельзя сделатьfinal, скрытая зависимость, хуже тестируется (нужно или Spring-контекст поднимать, или опять же рефлексией пихать).@Service public class OrderService { @Autowired private PaymentProcessor paymentProcessor; // Смотрите, какая хитрая жопа — поле приватное, а Spring туда залезет! } -
Через сеттер (setter injection): Что-то среднее. Не так плохо, как field injection, но и не так круто, как constructor. Удобно, если зависимость опциональная.
@Service public class OrderService { private PaymentProcessor paymentProcessor; @Autowired public void setPaymentProcessor(PaymentProcessor paymentProcessor) { this.paymentProcessor = paymentProcessor; // Установил и забыл } }
Ну и напоследок про аннотации, а то ты опять как Герасим мычать начнёшь:
@Autowired— основная, "дай сюда бин, какой найдёшь".@Qualifier— если бинов одного типа несколько, уточняешь, какой именно по имени.@Primary— помечаешь бин как основной по умолчанию для своего типа.@Resource/@Inject— аналоги@Autowiredиз других спецификаций (JSR-250, JSR-330). Почти то же самое, но со своими, блядь, нюансами.
Вот и вся магия. Контейнер Spring — он как тот самый немой Герасим: молчит, блядь, не говорит ничего, но работу делает. Найдёт все зависимости, свяжет их и выдаст тебе готовый, работающий объект. Главное — не проебиться с конфигурацией, а то будет не "Му-му", а полный пиздец.