Конфигурационные классы в 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.
Этот удобный процесс сокращает количество кода, который мы писали бы вручную. Согласитесь, мало кого бы обрадовало создание всех зависимостей руками.
Декларация зависимостей между бинами в явной форме
Если по каким-то причинам вы захотите явно указать бину 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-контейнер работать на вас.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ