JavaRush /Курси /Модуль 5. Spring /Як Spring автоматично налаштовує залежності

Як Spring автоматично налаштовує залежності

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

Сьогодні розберемося, як Spring автоматично налаштовує залежності, використовуючи механізм автосв'язування — autowiring. Готуйтесь, попереду багато магії (спойлер: насправді це просто грамотна архітектура).

Що таке autowiring?

Уявіть ситуацію: у додатку в вас є клас Car, якому потрібен об'єкт типу Engine. Раніше ми вручну визначали такі залежності через конструктори, сеттер-методи або поля. Але Spring дає механізм автоматичного зв'язування залежностей — autowiring. Це схоже на те, як ви замовляєте страву в кафе, а офіціант сам подбає, щоб потрібні інгредієнти опинилися на вашій тарілці. Зручно? Ще й як.

Autowiring дозволяє IoC-контейнеру Spring автоматично знаходити і зв'язувати біні на основі їх типу або імені. Це знімає з нас частину рутинної роботи по налаштуванню залежностей. Основний інструмент для цього — анотація @Autowired.

Приклад:


@Component
public class Car {

    private Engine engine;

    // Автоматичне зв'язування через конструктор
    @Autowired
    public Car(Engine engine) {
        this.engine = engine;
    }
}

@Component
public class Engine {
    // Порожній клас для прикладу
}

У цьому випадку Spring автоматично створить об'єкт Engine (якщо він є в контексті) і впровадить його в клас Car.


Стратегії автосв'язування

Spring використовує кілька стратегій для автосв'язування. Розгляньмо їх детальніше.

  1. За типом (byType)

    Spring шукає бин потрібного типу в контексті. Наприклад, якщо потрібен об'єкт Engine, то він шукає бин з таким типом.

    
    @Autowired
    private Engine engine; // Пошук біну типу Engine
    
  2. За іменем (byName)

    Якщо в контексті є кілька бінів одного типу, Spring може використовувати ім'я для зв'язування. Для цього використовують анотацію @Qualifier, про яку поговоримо трохи пізніше.


Проблема множинності: що робити, якщо бин потрібен один, а кандидатів — декілька?

Припустимо, у нас є два біна одного типу:


@Component
public class DieselEngine implements Engine {
}

@Component
public class ElectricEngine implements Engine {
}

Тепер клас Car потребує Engine. Що зробить Spring? Зб'ється з пантелику. Як ти стоїш перед полицею з десятком однакових зарядних пристроїв. У таких випадках Spring підніме виняток: NoUniqueBeanDefinitionException.

Але не переймайтесь: Spring дає інструменти для вирішення таких ситуацій.


Анотація @Qualifier

Щоб вказати конкретний бин, ми використовуємо анотацію @Qualifier. Це як ім'я в паспорті: якщо у двох людей однакове прізвище, дивимося на ім'я.

Приклад:


@Component
public class Car {

    private Engine engine;

    @Autowired
    public Car(@Qualifier("dieselEngine") Engine engine) {
        this.engine = engine;
    }
}

Тут ми явно вказали, що хочемо використовувати бин з іменем dieselEngine.

Звідки Spring знає, як називається бин? За замовчуванням ім'я біна співпадає з ім'ям класу з малої літери (наприклад, dieselEngine). Якщо потрібно задати кастомне ім'я, можна використовувати анотацію @Component("customName").


Анотація @Primary

Якщо у нас є кілька бінів одного типу, а ви хочете, щоб один з них використовувався "за замовчуванням", можна застосувати анотацію @Primary.

Приклад:


@Component
@Primary
public class ElectricEngine implements Engine {
}

@Component
public class DieselEngine implements Engine {
}

Тепер Spring за замовчуванням буде використовувати ElectricEngine, поки ми явно не вкажемо інший бин через @Qualifier.


Автосв'язування через поля, сеттери і конструктори

Ми вже говорили, що залежність можна впровадити через конструктор, сеттер або безпосередньо в поле. Давайте розберемо, як це поєднується з @Autowired.

Через конструктор

Це найбільш рекомендований спосіб впровадження залежностей, бо він робить об'єкти незмінними (immutable), що корисно для великих систем.


@Component
public class Car {

    private final Engine engine;

    @Autowired
    public Car(Engine engine) {
        this.engine = engine;
    }
}

Зверніть увагу: якщо у класу лише один конструктор, анотацію @Autowired можна не писати. Spring сам зрозуміє, який конструктор використовувати.


Через сеттер

Цей спосіб більш гнучкий, бо дозволяє змінювати залежності під час роботи програми, хоча це рідко потрібно.


@Component
public class Car {

    private Engine engine;

    @Autowired
    public void setEngine(Engine engine) {
        this.engine = engine;
    }
}

Через поле

Найбільш "магічний" спосіб, але й найспірніший, оскільки — ви вже здогадалися — він порушує принципи інкапсуляції.


@Component
public class Car {

    @Autowired
    private Engine engine;
}

Уникайте цього способу, особливо в великих проектах.


4. Типові помилки та їх виправлення

Проблема 1: Немає підходящого біна

Якщо Spring не може знайти бин для впровадження, він піднімає NoSuchBeanDefinitionException. Переконайтесь, що бин визначений, наприклад, за допомогою @Component або @Bean.

Проблема 2: Кілька бінів одного типу

Як ми вже обговорювали, в таких випадках Spring піднімає NoUniqueBeanDefinitionException. Це можна вирішити: просто додаємо @Qualifier або @Primary.


5. Практичний приклад

Зіб'ємо все, що дізнались, в одному прикладі. Створимо додаток, де є машина, якій потрібен двигун і коробка передач.


// Інтерфейс двигуна
public interface Engine {
    String getType();
}

// Реалізація бензинового двигуна
@Component
@Qualifier("petrolEngine") // Вказання імені біну
public class PetrolEngine implements Engine {
    @Override
    public String getType() {
        return "Бензиновий двигун";
    }
}

// Реалізація електричного двигуна
@Component
public class ElectricEngine implements Engine {
    @Override
    public String getType() {
        return "Електричний двигун";
    }
}

// Клас Car з впровадженими залежностями
@Component
public class Car {

    private final Engine engine;

    @Autowired
    public Car(@Qualifier("petrolEngine") Engine engine) {
        this.engine = engine;
    }

    public void start() {
        System.out.println("Машина працює з " + engine.getType());
    }
}

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

Завдяки механізму автосв'язування ви можете:

  1. Швидко переключатися між реалізаціями інтерфейсів.
  2. Спростити тестування, підміняючи залежність на mock.
  3. Сфокусуватися на бізнес-логіці замість того, щоб вручну підключати біни.

Отже, уявлення про автосв'язування отримали — поїхали далі: занурюємося в світ гнучкої та масштабованої розробки.

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