Тепер давайте розберемося з анотаціями на практиці, щоб стало зрозуміліше.
1. Анотація @Component — магія створення бінів
Анотація @Component перетворює звичайний Java-клас на Spring Bean, яким автоматично керує IoC-контейнер Spring. Це ніби ви доручили якомусь спецагенту Spring: "Слідкуй за цим класом, створюй його об'єкт і впроваджуй, коли знадобиться".
Під капотом IoC-контейнер сканує класи в проєкті за допомогою механізму Component Scan і шукає всі класи, позначені @Component.
Приклад використання @Component
import org.springframework.stereotype.Component;
// Анотацією показуємо, що цей клас — компонент (bean)
@Component
public class CoffeeMaker {
public String makeCoffee() {
return "☕ Кава готова!";
}
}
Тепер CoffeeMaker зареєстрований в IoC-контейнері. Ти можеш отримати його де завгодно, і Spring сам створить об'єкт цього класу для тебе.
Аналогія: якби ти був Spring-контейнером, то @Component — це знак "Додати це в список моїх обов'язків".
2. Анотація @Autowired: мистецтво впровадження залежностей
Коли ми позначаємо щось анотацією @Autowired, ми кажемо Spring: "Знайди і підключи потрібний компонент сам!". Spring шукає підходящий бин (наприклад, наш CoffeeMaker) і додає його через поле, конструктор або сеттер.
Впровадження через поле
Цей спосіб — найпростіший, але й недоліків у нього вистачає (див. "Типові помилки" нижче).
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Barista {
// Кажемо Spring: "Впровадь сюди CoffeeMaker!"
@Autowired
private CoffeeMaker coffeeMaker;
public void serveCoffee() {
System.out.println(coffeeMaker.makeCoffee());
}
}
Позначивши поле @Autowired, ми вказали Spring, що воно має бути ініціалізоване до першого виклику. Тепер, коли ми запустимо застосунок, Barista буде знати, як подавати каву.
Впровадження через конструктор
Рекомендований спосіб: він робить залежності "видимими", та й тестувати їх легко.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Barista {
private final CoffeeMaker coffeeMaker;
// Spring сам викличе цей конструктор і передасть CoffeeMaker
@Autowired
public Barista(CoffeeMaker coffeeMaker) {
this.coffeeMaker = coffeeMaker;
}
public void serveCoffee() {
System.out.println(coffeeMaker.makeCoffee());
}
}
Впровадження через сеттер
Підходить, якщо залежності можна змінювати в runtime (це потрібно рідко, але раптом).
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Barista {
private CoffeeMaker coffeeMaker;
@Autowired
public void setCoffeeMaker(CoffeeMaker coffeeMaker) {
this.coffeeMaker = coffeeMaker;
}
public void serveCoffee() {
System.out.println(coffeeMaker.makeCoffee());
}
}
Порада: якщо залежність обов'язкова, використовуй конструктор. Якщо ні — сеттер.
3. Анотація @Bean — створення кастомних бінів
Анотація @Bean потрібна для явного вказання Spring, що метод у класі-конфігурації створює бин. Цей підхід особливо корисний, якщо ти не можеш або не хочеш модифікувати вихідний клас (наприклад, якщо він зі сторонньої бібліотеки).
Приклад використання @Bean
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
// Вказуємо, що цей клас — Java-конфігурація
@Configuration
public class CoffeeConfig {
// Цей метод створює об'єкт CoffeeMaker
@Bean
public CoffeeMaker coffeeMaker() {
return new CoffeeMaker();
}
}
Тепер, замість @Component, ми явно прописали, що клас CoffeeMaker створюється через метод coffeeMaker. Spring все одно додасть його в IoC контейнер.
4. Практичне застосування всіх анотацій
Тепер давайте зберемо все разом у невеликий застосунок.
Клас 1: CoffeeMaker
import org.springframework.stereotype.Component;
@Component
public class CoffeeMaker {
public String makeCoffee() {
return "☕ Кава готова!";
}
}
Клас 2: Barista
import org.springframework.stereotype.Component;
import org.springframework.beans.factory.annotation.Autowired;
@Component
public class Barista {
private final CoffeeMaker coffeeMaker;
@Autowired
public Barista(CoffeeMaker coffeeMaker) {
this.coffeeMaker = coffeeMaker;
}
public void serveCoffee() {
System.out.println(coffeeMaker.makeCoffee());
}
}
Клас 3: CoffeeShopConfig (з використанням @Bean)
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class CoffeeShopConfig {
@Bean
public String coffeeRecipe() {
return "Рецепт кави: взяти каву, додати води та любові!";
}
}
Основний клас для запуску
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
@SpringBootApplication
public class CoffeeShopApplication {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(CoffeeShopApplication.class, args);
// Отримуємо Barista з IoC-контейнера
Barista barista = context.getBean(Barista.class);
barista.serveCoffee();
// Також доступний бин з рецептом кави
String recipe = context.getBean(String.class);
System.out.println(recipe);
}
}
5. Типові помилки і як їх уникнути
Відсутність біну
Якщо ти не позначиш клас @Component або не визначиш метод з @Bean, Spring не додасть його в IoC контейнер. В результаті отримаєш NoSuchBeanDefinitionException.
Багато кандидатів для @Autowired
Коли Spring знаходить два біна одного типу, він не знає, який використовувати. Тут допомагає анотація @Qualifier.
@Autowired
@Qualifier("specialCoffeeMaker")
private CoffeeMaker coffeeMaker;
Недоступні залежності
Якщо ти намагаєшся впровадити залежності через поле, але воно оголошене private і немає ні конструктора, ні сеттера, Spring не зможе виконати DI. Вихід очевидний: використовуй або конструктори, або сеттери.
Тепер ти знаєш, як працювати з анотаціями @Component, @Autowired і @Bean. Ці знання становлять основу написання гнучких і модульних застосунків у Spring Framework. Тепер справа за практикою.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ