JavaRush /Курси /Модуль 5. Spring /Основи IoC (Inversion of Control) і контейнер Spring

Основи IoC (Inversion of Control) і контейнер Spring

Модуль 5. Spring
Рівень 2 , Лекція 0
Відкрита

Тепер давайте заглибимось в одну з ключових концепцій — Inversion of Control (IoC).

Inversion of Control (IoC), або «інверсія управління», — це фундаментальна концепція, яка лежить в основі Spring Framework. Щоб розібратись, що це таке, почнемо з простої аналогії.

"Розумний" кошик у супермаркеті

Традиційний підхід (без IoC):

  • Ви шукаєте кожен продукт у каталозі самостійно
  • Пам'ятаєте, що зазвичай берете
  • При відсутності товару шукаєте заміну вручну
  • Самі відстежуєте акції на улюблені товари

"Розумний" кошик (з IoC):

  • Система аналізує ваші попередні покупки
  • Автоматично пропонує набір товарів
  • Сама підбирає аналоги при відсутності
  • Повідомляє про акції на ваші вподобання

Чому це IoC?

  1. Ви передаєте контроль над вибором продуктів системі
  2. Система сама впроваджує залежності (продукти) на основі ваших вподобань
  3. При зміні доступності товарів не потрібно змінювати свою поведінку
  4. Легко тестувати на різних наборах вподобань

У коді це приблизно виглядає так:


// Без IoC
class ManualCart {
    private Product milk = new RegularMilk(); // Жорстко задано
    private Product bread = new WhiteBread(); // Жорстко задано
}

// З IoC
@Component
class SmartCart {
    private final Product milk;  // Spring впровадить на основі
    private final Product bread; // вподобань користувача

    public SmartCart(@Qualifier("preferred") Product milk,
                     @Qualifier("preferred") Product bread) {
        this.milk = milk;
        this.bread = bread;
    }
}

У програмуванні IoC означає, що управління створенням об'єктів і їх налаштуванням передається спеціальному «менеджеру» — IoC-контейнеру. У випадку Spring цим займається ApplicationContext.


Як IoC змінює традиційний підхід до управління залежностями?

Розглянемо більш розгорнутий приклад. Давайте спочатку реалізуємо автомобіль без використання IoC, по-старому.

Нехай у нас є клас Car, і йому потрібен двигун — Engine — для роботи. У старому підході ви б написали щось на кшталт:


public class Engine {
    public void start() {
        System.out.println("Engine started");
    }
}

public class Car {
    private Engine engine;

    public Car() {
        this.engine = new Engine(); // Жорстко прописали створення об'єкта
    }

    public void start() {
        engine.start();
        System.out.println("Car started");
    }
}

Клас Car сам створює Engine. Проблеми очевидні:

  1. Клас Car в такому випадку залежить від конкретної реалізації Engine.
  2. Якщо реалізація Engine зміниться, доведеться змінювати код Car.
  3. Тестування класу Car стає складним, тому що неможливо просто замінити Engine на тестову версію.

З IoC (контейнер управління)

Тепер нехай контейнер Spring створює об'єкти за нас:


@Component
public class Engine {
    public void start() {
        System.out.println("Engine started");
    }
}

@Component
public class Car {
    private final Engine engine;

    // Spring впровадить залежність через конструктор
    public Car(Engine engine) {
        this.engine = engine;
    }

    public void start() {
        engine.start();
        System.out.println("Car started");
    }
}

Як це працює?

  • Ми сказали Spring, що Engine і Car — компоненти, і зробили це через анотацію @Component.
  • Spring сам створює об'єкт Engine і передає його в конструктор Car.
  • Якщо реалізація Engine зміниться, ми змінюємо тільки код Engine, а не код Car.

Це і є Inversion of Control: ми більше не контролюємо створення і управління залежностями. Це за нас робить контейнер Spring.


Роль IoC-контейнера в Spring

Що саме робить IoC-контейнер?

IoC-контейнер (в Spring це ApplicationContext) виконує три ключові задачі:

  1. Створення об'єктів (бінів): контейнер створює екземпляри всіх класів, позначених як компоненти.
  2. Управління залежностями: контейнер автоматично налаштовує залежності між бінами.
  3. Управління життєвим циклом: контейнер управляє створенням, ініціалізацією і знищенням об'єктів.

Типи IoC-контейнерів у Spring

  1. BeanFactory: базовий контейнер, який створює біни тільки за потреби.
  2. ApplicationContext: більш потужний контейнер, який містить додаткові функції (наприклад, роботу з подіями і підтримкою AOP).

Для більшості задач ми використовуємо ApplicationContext, бо він надає широкі можливості.


Приклад роботи IoC-контейнера Spring

Давайте розробимо невеликий приклад, щоб побачити, як Spring бере на себе управління об'єктами.

Крок 1: Створюємо біни за допомогою анотації @Component


@Component
public class Engine {
    public void start() {
        System.out.println("Engine started");
    }
}

@Component
public class Car {
    private final Engine engine;

    @Autowired // Кажемо Spring впровадити Engine
    public Car(Engine engine) {
        this.engine = engine;
    }

    public void start() {
        engine.start();
        System.out.println("Car started");
    }
}

Крок 2: Налаштовуємо Spring Boot


@SpringBootApplication
public class SpringIoCExample {
    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(SpringIoCExample.class, args);

        // Отримуємо бін Car з контейнера
        Car car = context.getBean(Car.class);
        car.start(); // Запуск автомобіля
    }
}

Крок 3: Результат виконання


Engine started
Car started

Знайомство з ключовими інтерфейсами IoC-контейнера

BeanFactory

BeanFactory — це найпростіший IoC-контейнер Spring. Він створює біни тільки за запитом. У реальному житті ми його використовуємо рідко через обмежені можливості (наприклад, відсутність подій).


BeanFactory factory = new XmlBeanFactory(new FileSystemResource("beans.xml"));

ApplicationContext

ApplicationContext — це стандартний IoC-контейнер у Spring. Він додає багато корисних функцій:

  • Автоматичне зв'язування залежностей.
  • Обробка подій.
  • Підтримка міжнародних повідомлень (i18n).
  • Вбудована інтеграція з AOP.

З ApplicationContext ми найчастіше працюємо у вигляді Spring Boot-додатків.


6. Переваги IoC

Ослаблення зв'язності Класи не залежать один від одного безпосередньо, що спрощує рефакторинг і заміну частин системи.

Тестованість Ви можете легко замінювати реальні залежності на моки для тестування.

Масштабованість IoC-контейнер дозволяє легко додавати і налаштовувати нові залежності.

Управління життєвим циклом Контейнер бере на себе завдання зі створення, ініціалізації і знищення об'єктів.


Типові помилки і підводні камені

Під час роботи з IoC ви можете зустріти кілька поширених проблем:

  • Пропущена анотація @Component: якщо забути вказати, що клас є компонентом, Spring просто не знайде його.
  • Циклічні залежності: якщо два біна залежать один від одного, Spring видасть помилку.
  • Неправильне налаштування конфігурації: якщо ви забули вказати пакет для сканування компонентів (@ComponentScan), то ваші біни не будуть знайдені.

Тепер, коли ви розумієте, як працює IoC і його контейнер у Spring, у вас в руках опинився потужний інструмент для створення гнучких і легко підтримуваних додатків. Готові заглибитись у Dependency Injection? Тоді переходимо до наступної лекції!

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ