Почему Spring стал популярным — автоматизация DI и IoC на практике
Содержание
Переработанная версия
Это обновлённая версия моей старой статьи, изначально опубликованной 14.01.2024. Исправлены ошибки, убраны неточности, удалены лишние примеры, а весь текст полностью отформатирован для лучшей читаемости.
Одной из причин, почему Spring Framework получил массовое распространение в начале 2000-х, стала его способность автоматизировать применение принципов инверсии управления (IoC) и внедрения зависимостей (DI).
Эти принципы существовали и до Spring, но внедрялись вручную, что требовало большого количества кода и дисциплины. Spring же предложил контейнер, который сам создаёт объекты, настраивает их и соединяет между собой, избавляя разработчиков от рутинной работы.
Как было до Spring
В эпоху раннего Java EE (J2EE) DI приходилось делать вручную. Объекты часто создавали свои зависимости сами, что приводило к сильной связанности.
public class OrderService {
private InventoryService inventoryService = new InventoryServiceImpl();
public void processOrder(Order order) {
if (inventoryService.isAvailable(order.getItem())) {
// process the order
}
}
}
Такая конструкция порождала жёсткую зависимость от конкретной реализации InventoryServiceImpl
. Усложняла тестирование
из-за невозможности просто подставить мок. В случае необходимости замены реализации, приходилось менять код класса.
«Ручной» DI (ещё без Spring)
Опытные команды уже тогда практиковали IoC/DI — просто делали это вручную:
public class OrderService {
private final InventoryService inventoryService;
public OrderService(InventoryService inventoryService) {
this.inventoryService = inventoryService;
}
public void processOrder(Order order) {
if (inventoryService.isAvailable(order.getItem())) {
// process the order
}
}
}
Теперь зависимость передаётся извне, и мы можем легко подставить любую реализацию.
Минус такого подхода в том, что в больших проектах приходилось вручную создавать и “связывать” десятки, а то и сотни объектов, что в свою очередь приводило к ошибкам во время написания кода.
Как стало со Spring
Spring предложил IoC-контейнер, который:
- сам создаёт бины (объекты),
- управляет их жизненным циклом,
- автоматически внедряет зависимости.
@Service
public class OrderService {
private final InventoryService inventoryService;
@Autowired
public OrderService(InventoryService inventoryService) {
this.inventoryService = inventoryService;
}
public void processOrder(Order order) {
if (inventoryService.isAvailable(order.getItem())) {
// process the order
}
}
}
Что изменилось:
- Не нужно вручную создавать
InventoryService
и передавать его вOrderService
. - Spring на старте приложения найдёт все реализации
InventoryService
и автоматически внедрит нужную. - При смене реализации достаточно зарегистрировать новый бин — код
OrderService
менять не придётся.
Почему это “выстрелило” в 2000-х
За счёт автоматизации DI и IoC Spring добился следующих результатов:
- Масштабируемость — контейнер сам создаёт и связывает сотни объектов.
- Гибкость — легко подменять реализации через конфигурацию, а не через переписывание кода.
- Сокращение рутины — меньше ручного кода для “установки” зависимостей.
- Постепенное внедрение — можно было использовать Spring только для IoC/DI, не переписывая весь проект.
DI и IoC были известны и до Spring, но именно Spring сделал их массовыми и удобными для повседневной Java-разработки, убрав рутину и минимизировав человеческий фактор при “установке” зависимостей.