Конфігураційні класи в Java замінюють старий підхід з XML-конфігурацією. Завдяки цьому налаштування Spring-застосунків стає більш читабельним і зручним.
Конфігураційний клас — це Java-клас, який використовується для опису конфігурації вашого застосунку. У Spring такі класи помічають анотацією @Configuration. Це дає IoC-контейнеру зрозуміти, що клас містить методи, які створюють біни. Ці методи помічені анотацією @Bean.
Конфігураційний клас можна уявити як меню вашої улюбленої кафешки. За допомогою @Bean ви описуєте, що буде подано на стіл (об'єкти), а IoC-контейнер виступає офіціантом, який доставить усе потрібне (впровадження залежностей) до вашого столика.
Приклад простого конфігураційного класу
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Bean
public String welcomeMessage() {
return "Ласкаво просимо в Spring Framework!";
}
}
У нашому прикладі welcomeMessage — це бін, який повертає рядок. Конфігураційний клас AppConfig містить метод, помічений анотацією @Bean, а Spring знає, що цей метод створює бін, яким керує IoC-контейнер.
Але не хвилюйтеся, ми не зупинимося на біні, який просто говорить вам "привіт". Ми ще розженемося і створимо складніші конфігурації.
Створення бінів і робота із залежностями
Конфігураційні класи корисні при створенні об'єктів, які мають залежності. Наприклад, якщо в нас є клас Car, і він залежить від двигуна Engine, ми можемо налаштувати це в конфігураційному класі.
Приклад із залежностями
public class Engine {
private String type;
public Engine(String type) {
this.type = type;
}
public String getType() {
return type;
}
}
public class Car {
private Engine engine;
public Car(Engine engine) {
this.engine = engine;
}
public void drive() {
System.out.println("Car is driving with " + engine.getType() + " engine.");
}
}
Тепер створимо конфігураційний клас для цих об'єктів:
@Configuration
public class VehicleConfig {
@Bean
public Engine engine() {
return new Engine("V8");
}
@Bean
public Car car() {
return new Car(engine());
}
}
У методі car ми викликаємо метод engine, щоб передати його в конструктор класу Car. Це дозволяє Spring зрозуміти, що бін Car залежить від біна Engine. Тепер IoC-контейнер потурбується про те, щоб правильно налаштувати наш об'єкт.
Як це працює?
- Spring створює бін
Engineз типомV8. - Потім створюється бін
Car, і в його конструктор автоматично передається бінEngine.
Цей зручний процес скорочує кількість коду, який ми писали б вручну. Чесно — мало кого би порадувало створювати всі залежності вручну.
Декларація залежностей між бінaми в явній формі
Якщо з якоїсь причини ви захочете явно вказати біну Car, що йому потрібен бін Engine, це можна зробити з анотацією @Qualifier (спойлер: знадобиться в наступних лекціях).
Складні залежності: біни, що залежать від декількох інших бінів
Припустимо, ми створюємо сервіс бронювання авіаквитків. Потрібно об'єднати кілька залежностей, наприклад клієнта для роботи з базою даних і сервіс оплати. Все це можна налаштувати в конфігураційному класі.
public class PaymentService {
public void pay() {
System.out.println("Платіж успішно оброблено!");
}
}
public class BookingService {
private PaymentService paymentService;
public BookingService(PaymentService paymentService) {
this.paymentService = paymentService;
}
public void bookFlight() {
System.out.println("Бронювання завершено.");
paymentService.pay();
}
}
Налаштуємо все це за допомогою конфігураційного класу:
@Configuration
public class BookingConfig {
@Bean
public PaymentService paymentService() {
return new PaymentService();
}
@Bean
public BookingService bookingService() {
return new BookingService(paymentService());
}
}
Тепер ми отримали:
- Інкапсуляцію логіки створення об'єктів.
- Мінімізацію коду в основній логіці застосунку.
- Гнучкість: зміни в конфігурації легко адаптуються.
Запобігаємо проблемам із дублюванням бінів
Однією з поширених помилок є випадкове створення дублікатів бінів. Якщо ви спробуєте оголосити два біна з однаковим ім'ям, Spring видасть помилку: "Знайдено кілька кандидатів для впровадження залежності". Щоб цього уникнути, переконайтеся, що ваші методи @Bean мають унікальні імена і коректно описані.
Практика: налаштування реального проєкту
Давай зробимо крок у бік нашого навчального застосунку. Ми будемо створювати сервіс для керування книжками в бібліотеці, з використанням конфігураційного класу.
Бізнес-логіка
public class Book {
private String title;
private String author;
public Book(String title, String author) {
this.title = title;
this.author = author;
}
@Override
public String toString() {
return "Book: " + title + " by " + author;
}
}
public class LibraryService {
private Book book;
public LibraryService(Book book) {
this.book = book;
}
public void displayBookInfo() {
System.out.println(book.toString());
}
}
Конфігураційний клас
@Configuration
public class LibraryConfig {
@Bean
public Book defaultBook() {
return new Book("Clean Code", "Robert C. Martin");
}
@Bean
public LibraryService libraryService() {
return new LibraryService(defaultBook());
}
}
Використання у головному застосунку
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Main {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(LibraryConfig.class);
LibraryService libraryService = context.getBean(LibraryService.class);
libraryService.displayBookInfo();
}
}
Підсумковий вивід програми:
Book: Clean Code by Robert C. Martin
Вітаю! Ми розглянули, як використовувати конфігураційні класи і створювати Spring Beans. Тепер ви можете конструювати об'єкти, зв'язувати їхні залежності і змусити IoC-контейнер працювати на вас.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ