🍃 Розділ 5 · Питання #20

Що таке автоконфігурація в Spring Boot

Принцип роботи формулюється як Convention over Configuration (Конвенція понад конфігурацію): замість написання сотень рядків XML або Java-конфігурації Spring Boot бере на себе р...


🟢 Junior Level

Автоконфігурація (Auto-Configuration) — це ключовий механізм Spring Boot, який автоматично створює, зв’язує та налаштовує біни в ApplicationContext на основі бібліотек, виявлених у classpath, системних властивостей (application.properties / application.yml) та вже оголошених розробником компонентів.

Принцип роботи формулюється як Convention over Configuration (Конвенція понад конфігурацію): замість написання сотень рядків XML або Java-конфігурації Spring Boot бере на себе рутину з розумними налаштуваннями за замовчуванням (Opinionated Defaults).

Базовий приклад: підключення БД

У класичному Spring Framework для підключення до PostgreSQL потрібно було вручну налаштовувати DataSource, EntityManagerFactory, TransactionManager та пул HikariCP:

// Класичний Spring (без Spring Boot) — десятки рядків шаблонного коду:
@Configuration
public class ManualDbConfig {
    @Bean
    public DataSource dataSource() {
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl("jdbc:postgresql://localhost:5432/mydb");
        config.setUsername("user");
        config.setPassword("pass");
        return new HikariDataSource(config);
    }
}

У Spring Boot достатньо:

  1. Додати стартер у pom.xml / build.gradle: spring-boot-starter-data-jpa та драйвер postgresql.
  2. Вказати параметри підключення в application.yml:
    spring:
      datasource:
        url: jdbc:postgresql://localhost:5432/mydb
        username: user
        password: pass
    
  3. Spring Boot сам виявить клас org.postgresql.Driver і бібліотеку HikariDataSource в classpath та створить готовий до роботи бін DataSource.

Як вимкнути непотрібну автоконфігурацію

// Вимкнення через анотацію:
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Або через конфігураційний файл:

spring:
  autoconfigure:
    exclude:
      - org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration

🟡 Middle Level

Пайплайн запуску автоконфігурації

Процес запуску автоконфігурації координується мета-анотацією @SpringBootApplication:

@SpringBootApplication
         │
         ├──> @Configuration
         ├──> @ComponentScan (Сканує користувацькі біни)
         └──> @EnableAutoConfiguration
                   │
                   ▼
         AutoConfigurationImportSelector
                   │
                   ▼
         Читання реєстру автоконфігурацій (.imports / spring.factories)
                   │
                   ▼
         Фільтрація за умовами (@Conditional)
                   │
                   ▼
         Реєстрація конфігурацій, що залишилися, в ApplicationContext

Сімейство анотацій @Conditional

Автоконфігурація працює завдяки умовним анотаціям (Conditions). Бін створюється тільки у разі виконання певного критерію:

Анотація Умова спрацьовування
@ConditionalOnClass(Foo.class) Клас Foo фізично знайдено в classpath.
@ConditionalOnMissingClass Класу немає в classpath.
@ConditionalOnBean(Bar.class) У контексті вже є хоча б один бін зазначеного типу.
@ConditionalOnMissingBean У контексті ще немає біна такого типу (дозволяє розробнику перевизначити бін).
@ConditionalOnProperty В application.yml задано властивість із певним значенням.
@ConditionalOnWebApplication Додаток є веб-додатком (сервлетним або реактивним).

Приклад із вихідного коду Spring Boot: JacksonAutoConfiguration

@AutoConfiguration
@ConditionalOnClass(ObjectMapper.class) // Спрацює, лише якщо підключено Jackson
public class JacksonAutoConfiguration {

    @Bean
    @Primary
    @ConditionalOnMissingBean // Створить ObjectMapper ТІЛЬКИ якщо розробник не оголосив свій!
    public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
        return builder.createXmlMapper(false).build();
    }
}

Золоте правило: Анотація @ConditionalOnMissingBean — фундамент гнучкості Spring Boot. Вона означає: «Якщо програміст не оголосив власний бін, контейнер надасть дефолтний».

Налагодження автоконфігурації: Condition Evaluation Report

Щоб точно зрозуміти, чому певний бін створився або був пропущений, запустіть додаток із прапорцем --debug:

java -jar myapp.jar --debug

У консоль виведеться докладний звіт CONDITIONS EVALUATION REPORT:

  • Positive matches: автоконфігурації, чиї умови виконано.
  • Negative matches: автоконфігурації, які було пропущено (із зазначенням точної причини, наприклад: @ConditionalOnClass did not find [org.apache.kafka.clients.producer.Producer]).
  • Exclusions: виключені конфігурації.

🔴 Senior Level

Еволюція механізмів: Spring Boot 2.x vs Spring Boot 3.x

До версії Spring Boot 2.7 для виявлення автоконфігурацій використовувався файл META-INF/spring.factories. У Spring Boot 3.x цей механізм було замінено на спеціалізований файл реєстру:

Spring Boot 2.x:
  META-INF/spring.factories
  Ключ: org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
        com.example.MyAutoConfiguration

Spring Boot 3.x (та 2.7+):
  META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
  Вміст: простий список повних імен класів по одному на рядок:
        com.example.MyAutoConfiguration

Чому формат було змінено:

  1. Підтримка GraalVM Native Image та Spring AOT: простий список класів набагато швидше аналізується статичними аналізаторами під час Ahead-Of-Time компіляції.
  2. Продуктивність холодного старту: виключено оверхед на парсинг важкого Properties-файлу з багатьма різними SPI-ключами.

Як Spring Boot перевіряє @ConditionalOnClass без ClassNotFoundException

Якби Spring Boot перевіряв наявність класів через звичайний Class.forName("org.postgresql.Driver"), відсутній драйвер призвів би до викидання винятку ClassNotFoundException та аварійної зупинки додатка.

Внутрішній механізм:

Spring Boot аналізує метадані класів автоконфігурації через низькорівневий парсер байткоду ASM (SimpleMetadataReaderFactory), не завантажуючи самі класи в JVM Metaspace! Якщо ASM бачить, що цільовий тип відсутній у ClassLoader, гілка конфігурації відкидається ще до завантаження класу конфігурації.

Порядок фаз: Чому @ComponentScan виконується ДО автоконфігурації

Це фундаментальний архітектурний принцип:

  1. Спочатку відпрацьовує @ComponentScan: контейнер знаходить і реєструє всі користувацькі біни, позначені @Component, @Service, @Repository, @Bean.
  2. Тільки після цього запускається AutoConfigurationImportSelector.
  3. Завдяки цьому, коли автоконфігурація доходить до умов @ConditionalOnMissingBean, вона вже «бачить» усі біни, створені розробником, і поступається їм пріоритетом.

Управління порядком автоконфігурацій: @AutoConfiguration

Для координації залежностей між різними автоконфігураціями використовується анотація @AutoConfiguration (яка прийшла на зміну @Configuration у стартерах):

package com.example.autoconfigure;

import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.annotation.Bean;

@AutoConfiguration(after = DataSourceAutoConfiguration.class) // Запуститься СТРОГО ПІСЛЯ бази даних
@ConditionalOnClass(CustomAuditClient.class)
public class CustomAuditAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public CustomAuditClient customAuditClient() {
        return new CustomAuditClient();
    }
}

🎯 Шпаргалка для інтерв’ю

30-секундна відповідь

Автоконфігурація — механізм Spring Boot, що реалізує підхід Convention over Configuration. Він автоматично реєструє біни в контексті на основі бібліотек у classpath, властивостей конфігурації та поточного оточення.

  • Ініціюється мета-анотацією @EnableAutoConfiguration через клас AutoConfigurationImportSelector.
  • У Spring Boot 3.x список автоконфігурацій зчитується з файлу META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports.
  • Реєстрація бінів керується сімейством анотацій @Conditional (головна з яких — @ConditionalOnMissingBean, яка дозволяє користувачеві перевизначати дефолтні біни).
  • Користувацькі біни з @ComponentScan реєструються першими, а автоконфігурація підключається після них.

4 каверзних питання з відповідями

  1. Як Spring Boot перевіряє умови @ConditionalOnClass, не викликаючи ClassNotFoundException? Відповідь: Spring Boot використовує бібліотеку ASM для читання байткоду (.class файлів) без фактичного завантаження класів у пам’ять через ClassLoader. За допомогою MetadataReader контейнер аналізує константний пул та сигнатури типів. Якщо клас не може бути розв’язаний ClassLoader-ом, умова бракується без фатальної помилки.

  2. Чому автоконфігурації завжди запускаються ПІСЛЯ користувацьких класів конфігурації? Відповідь: Щоб розробник додатка мав найвищий пріоритет. Спочатку виконується сканування компонентів додатка (@ComponentScan). Коли всі кастомні біни зареєстровано, запускається автоконфігурація, в якій спрацьовують анотації @ConditionalOnMissingBean. Якщо користувач уже створив свій бін, автоконфігурація мирно відступає.

  3. У чому різниця між META-INF/spring.factories та .imports-файлом у Spring Boot 3? Відповідь: У Spring Boot 2.x усі точки розширення (включно з EnableAutoConfiguration) зберігалися в спільному файлі spring.factories формату Java Properties. У Spring Boot 3.x автоконфігурації винесено в окремий текстовий файл META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports, що являє собою простий список класів. Це прискорює старт додатка і необхідно для підтримки компіляції в Native Image (GraalVM / Spring AOT).

  4. Як визначити, чому конкретна автоконфігурація не застосувалася до вашого проєкту? Відповідь: Запустити додаток із параметром --debug (або debug=true в application.properties) чи викликати ендпоінт Spring Boot Actuator /actuator/conditions. У консолі з’явиться ConditionEvaluationReport, де в розділі Negative matches зазначено конкретну невиконану умову (наприклад, відсутність класу або властивості).

Червоні прапорці (чого категорично не можна говорити)

  • ❌ «Автоконфігурація створює абсолютно всі біни з усіх стартерів у пам’яті» — створюються лише ті біни, для яких виконано всі умови @Conditional.
  • ❌ «У Spring Boot 3.x для написання стартера все ще потрібно використовувати spring.factories» — у Boot 3.x автоконфігурації оголошуються через .imports-файли.
  • ❌ «Автоконфігурація сповільнює виконання HTTP-запитів у рантаймі» — автоконфігурація працює строго один раз під час старту додатка і ніяк не впливає на продуктивність у runtime.
  • ❌ «@ConditionalOnMissingBean сканує бібліотеки в classpath» — ця анотація перевіряє наявність біна в поточному ApplicationContext, а не файли на диску.

Пов’язані теми