JavaRush /Курсы /Spring Boot /Auto-configuration: сборка платформы

Auto-configuration: сборка платформы

Spring Boot
8 уровень , 0 лекция
Открыта

1. Эффект «магии» в Spring Boot

Если вы в первый раз видите Spring Boot, то ощущение примерно такое: вы пишете одну строку SpringApplication.run(...), а в консоли внезапно оживает целая вселенная — логи, сервер, какой-то порт, куча сообщений. Это нормальная реакция. Наш мозг не любит, когда маленькое действие вызывает большой эффект — он сразу подозревает колдовство.

Свои @Service, @Repository и constructor wiring мы уже умеем собирать сами. Но на старте Boot-приложения в контексте оказывается гораздо больше, чем наш код. Значит, кроме прикладного wiring есть ещё один слой сборки — платформенный, и сегодня важно понять, как он вообще работает.

Spring Boot действительно делает за вас много работы, но не потому что он «волшебный», а потому что он платформа, которая собирает типовую инфраструктуру за вас. Представьте, что вы строите дом. Вы можете вручную тянуть провода, ставить щиток, подбирать автоматы, прокладывать трубы. А можете купить хороший домокомплект, где всё типовое уже продумано. Дом от этого не становится «магическим» — просто часть решений вынесена в готовый набор.

Мы уже видели, что Spring — это не «набор аннотаций», а контейнер. Теперь добавляем следующий слой: Boot — это не «ещё одна аннотация», а платформенная сборка конфигурации, которая подключается к вашему контейнеру.

2. Auto-configuration простыми словами

Сейчас будет важное определение, которое стоит запомнить именно в таком смысле, а не как формулировку «для экзамена». Auto-configuration в Spring Boot — это механизм, который подключает к вашему приложению набор готовых Spring-конфигураций, чтобы типовая инфраструктура появилась автоматически, без того чтобы вы каждый раз писали её руками.

Ключевой момент: auto-configuration — это не отдельный «секретный режим Spring». Это буквально обычные @Configuration-классы, только они живут внутри библиотек Spring Boot (и связанных модулей) и подключаются автоматически при старте приложения. То есть это такой же Spring-код, просто не ваш.

Очень полезно держать в голове простую «картинку слов»:

  • Обычная конфигурация — это когда вы сами написали @Configuration и @Bean, и этот код точно войдёт в контекст.
  • Auto-конфигурация — это когда Spring Boot может подключить готовую конфигурацию, если она подходит текущему приложению (по набору сигналов: зависимости, настройки, состояние контекста). Важно: «может», а не «обязан».

Если сказать ещё проще: Boot — это «сборщик платформы». Он не пишет вашу бизнес-логику, но умеет привезти вам готовые “кирпичики” инфраструктуры, чтобы вы начали делать полезное быстрее и в более предсказуемой форме.

3. Auto-configuration в ApplicationContext

Давайте аккуратно уберём одну очень распространённую иллюзию: будто у Boot есть «второй контейнер», «особая память» или «скрытый слой». Нет. Всё, что Boot настраивает, в итоге выражается в бинах, которые попадают в тот же ApplicationContext, где живут ваши @Service и @Repository.

Самое наглядное место, с которого начинается жизнь Boot-приложения — ваш main-класс:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication // Включает компонент-сканирование и auto-configuration
public class CatalogServiceApplication {

    public static void main(String[] args) {
        // Точка входа приложения: дальше Boot соберёт ApplicationContext и инфраструктуру
        SpringApplication.run(CatalogServiceApplication.class, args);
    }
}

С точки зрения «обычной Java» это выглядит как один вызов. Но с точки зрения Boot это старт процесса, в котором создаётся ApplicationContext, поднимается окружение (environment), подключаются конфигурации, регистрируются бины — и затем приложение выходит в режим «готово».

Чтобы почувствовать это руками, можно сделать очень простой эксперимент: после старта вывести число определений бинов в контексте. Это не «количество классов», а количество зарегистрированных компонентов и настроек, которые знает контейнер.

По ходу дня нам будут встречаться короткие probe-компоненты. Это не новая постоянная архитектура catalog-service, а одноразовые фонарики: держим в проекте по одному, запускаем, смотрим вывод и убираем, чтобы startup не превратился в шумный чат из диагностических runner’ов.

Добавим временный runner в проект (например, в пакет catalog.bootstrap, чтобы он логически был рядом со стартовыми штуками):

import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

@Component // Делаем класс бином, чтобы Boot запустил его при старте
class ContextSummaryRunner implements ApplicationRunner {

    private final ApplicationContext context;

    // Внедряем контекст, чтобы задавать ему вопросы о зарегистрированных бинах
    ContextSummaryRunner(ApplicationContext context) {
        this.context = context;
    }

    @Override
    public void run(ApplicationArguments args) {
        // Выводим количество определений бинов в контейнере (число зависит от зависимостей)
        System.out.println("Bean definitions: " + context.getBeanDefinitionCount());
        // Например: Bean definitions: 340
    }
}

Когда вы это запустите, вы почти наверняка увидите число, которое гораздо больше количества классов, которые вы написали сами. И это хорошо: это означает, что Boot реально собрал инфраструктуру за вас. Важная мысль: «в контексте много бинов» — не баг, а нормальное состояние Boot-приложения.

4. Auto-configuration как сборка платформы

Теперь полезно собрать простую рабочую картину, которая потом сильно экономит нервы. Контекст вашего приложения — это не «наше приложение плюс чуть-чуть Spring». Это место, где встречаются три источника бинов: ваши компоненты, ваши явные конфигурации и auto-configuration от Boot.

Очень удобно представить это как «общую корзину», куда разные участники кладут свои вещи:

flowchart TD
    A["Ваш код (catalog-service) @Service, @Repository, @Component"] --> C["ApplicationContext"]
    B["Ваши явные конфигурации @Configuration + @Bean"] --> C
    D["Spring Boot auto-configuration готовые @Configuration-классы из Boot"] --> C
    C --> R["Запущенное приложение"]

В этой схеме нет «магической зоны». Auto-configuration — это просто третий источник, который кладёт инфраструктурные бины в тот же контейнер.

И тут возникает важный бытовой вопрос: а как понять, какие бины “наши”, а какие “пришли с Boot”? Не по тому, кто их «создал», а по смыслу ответственности. И это как раз то, чему Boot приучает: разделять прикладной код и инфраструктуру.

5. Прикладные и инфраструктурные бины

Сейчас мы подойдём к границе, которая отделяет здоровое Boot-приложение от вечного хаоса. У вас в проекте будут жить два больших класса бинов. Они оба «настоящие» и оба важны, но вы относитесь к ним по‑разному: прикладные вы пишете и развиваете, инфраструктурные — в основном принимаете как платформу и трогаете аккуратно.

Чтобы не растекаться по терминам, зафиксируем их человечески.

Прикладные бины — это то, что относится к вашему домену. В catalog-service это сервисы и репозитории каталога, модели домена, стартовые «сидеры» данных (если они у вас есть) и подобные вещи. Boot их не должен «придумывать», потому что он не знает ваш бизнес.

Например, наш сервис каталога — это прикладной бин:

import org.springframework.stereotype.Service;

@Service // Компонент прикладного слоя: доменная/бизнес-логика
public class CourseCatalogService {

    private final CourseCatalogRepository repository;

    public CourseCatalogService(CourseCatalogRepository repository) {
        // Внедряем прикладную зависимость (репозиторий) через конструктор
        this.repository = repository;
    }
}

И репозиторий (в нашем учебном варианте — in-memory) тоже прикладной бин. Он не «инфраструктура Spring», он часть приложения:

import java.util.List;
import org.springframework.stereotype.Repository;

@Repository // Прикладной репозиторий (в примере — простая in-memory реализация)
public class InMemoryCourseCatalogRepository implements CourseCatalogRepository {

    @Override
    public List<CourseCard> findAll() {
        // Заглушка: в реальном проекте тут будет доступ к данным
        return List.of(); // для примера достаточно пустого списка
    }
}

Инфраструктурные бины — это то, что делает приложение «живым»: встроенный сервер, обработка HTTP, JSON, конвертеры, настройки кодеков, обработчики ошибок, логирование, метрики и так далее. Даже без контроллеров вы уже могли заметить, что Boot пишет в логи про сервер и порты. Это и есть проявление инфраструктуры.

Вот краткая таблица для ощущения границы:

Тип бинов Примеры в catalog-service Кто «главный автор»
Прикладные (domain/application) CourseCatalogService, InMemoryCourseCatalogRepository, ваши Runner-классы Вы (и команда проекта)
Инфраструктурные (platform) встроенный сервер, базовая web-инфраструктура, JSON-мэппинг, внутренние настройки Spring Spring Boot + Spring Framework

Важно: инфраструктурные бины — не «чужие и страшные». Они просто не должны быть вашей ежедневной головной болью. Если вы каждую неделю переписываете инфраструктуру, вы не делаете продукт — вы строите свой мини-фреймворк. А это очень дорого и редко оправдано, особенно на junior-уровне.

6. «Мало кода» и работа на старте

Есть тонкая ловушка мышления: раз я написал одну строку run(...), значит приложение «почти ничего не делает». А потом студент удивляется: «Почему старт занимает секунды? Почему столько классов? Почему такая простыня логов?» Здесь нужно переключить модель: Boot экономит ваш ручной труд, но он не отменяет физику процесса старта.

Приложению всё равно нужно:

  • создать контейнер;
  • зарегистрировать и связать бины;
  • поднять сервер (если это web-приложение);
  • подготовить нужные настройки;
  • подготовить инфраструктуру сериализации и обработки запросов (если приложение работает как web-сервис);
  • и только потом перейти в состояние «готово».

Boot сокращает не «работу компьютера», а «работу разработчика». Он даёт шаблонные решения и “sensible defaults” (разумные дефолты), чтобы вы не собирали базовую платформу заново в каждом проекте.

Если представить альтернативную реальность без Boot, то в ней вы бы в каждом проекте снова и снова принимали десятки одинаковых решений: какой сервер, как настраивать JSON, как устроить базовую обработку ошибок, как организовать типовые интеграции. Можно, конечно. Но тогда ваша профессия будет называться «собиратель инфраструктуры руками», а не «backend-разработчик, который решает задачи бизнеса». Boot как раз и появился, чтобы от этого избавлять.

7. Мини‑проверка: «свои» бины

Давайте закрепим главное ощущение: в Boot-приложении бинов много, но «своих» по смыслу — мало. И это нормально: ваше приложение обычно маленькое, а инфраструктура вокруг него — довольно толстая.

Можно вывести имена бинов определённого типа. Например, убедимся, что CourseCatalogService в контексте действительно один (и мы не получили случайно «зоопарк» из десяти сервисов одного типа). Это снова именно временный probe: он нужен, чтобы задать контейнеру один конкретный вопрос и получить ответ.

import java.util.Arrays;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

@Component
class AppBeansProbeRunner implements ApplicationRunner {

    private final ApplicationContext context;

    AppBeansProbeRunner(ApplicationContext context) {
        this.context = context;
    }

    @Override
    public void run(ApplicationArguments args) {
        String[] names = context.getBeanNamesForType(CourseCatalogService.class);
        System.out.println(Arrays.toString(names)); // [courseCatalogService]
    }
}

Это маленький, но очень полезный стиль мышления. Вы не пытаетесь «угадать», что произошло. Вы задаёте контейнеру конкретный вопрос: «Какие бины вот этого типа у тебя есть?» Если ответ неожиданный — значит, вы узнали что-то важное о сборке приложения.

Обратите внимание на ещё один момент: мы спрашиваем про наш прикладной тип. И это принципиально проще, чем пытаться сходу разобраться во всех инфраструктурных бинах. Ваша главная зона ответственности — прикладные бины. Инфраструктуру вы учитесь понимать постепенно, но не обязаны контролировать всю сразу.

8. Граница ответственности Boot и catalog-service

Сейчас я скажу вещь, которая очень помогает перестать спорить с фреймворком (и с жизнью). Boot делает за вас то, что можно сделать «типово», а вы делаете то, что относится к вашему домену. Когда вы путаете эти зоны, обычно происходят два сценария.

В первом сценарии вы ждёте от Boot невозможного: «Пусть Boot сам создаст мне сервис, репозиторий, загрузит курсы, настроит фильтрацию, и вообще пусть ещё бизнес за меня придумает». Это, увы, пока не входит в функциональность Spring Boot (и слава богу: иначе мы бы все работали в Boot, который пишет стартапы и увольняет людей).

Во втором сценарии вы, наоборот, пытаетесь утащить под ручной контроль то, что Boot уже делает хорошо, просто потому что оно пока непривычно. И тут начинается «я перепишу половину инфраструктуры, потому что не понимаю, откуда взялся этот бин». Это тоже плохая стратегия.

Здоровая граница для catalog-service сейчас выглядит так: мы явно описываем прикладной wiring (servicerepository), держим код читаемым, не расползаемся в god-объекты, а инфраструктурную базу принимаем как платформу, которую Boot собирает автоматически. Наша задача сегодня — перестать называть это магией и начать называть это сборкой по правилам.

9. Типичные ошибки при работе с auto-configuration

Ошибка №1: «Boot сам создаёт вообще всё, значит мне почти ничего писать не надо».
Обычно это заканчивается тем, что человек не пишет прикладные компоненты или пишет их “где-то как-то”, ожидая, что платформа «додумает». Boot действительно создаёт много бинов, но это почти всегда инфраструктура. Ваш CourseCatalogService и ваша доменная логика — это ответственность приложения. Если вы их не описали, Boot не обязан угадывать ваш бизнес.

Ошибка №2: «Если в логах куча строк, значит Spring что-то делает не так».
На самом деле большая часть старта — это нормальная работа контейнера: регистрация бинов, подготовка инфраструктуры, поднятие runtime. Много логов — не автоматически “плохо”. Плохо — когда вы не понимаете, что они означают. Сегодня мы как раз делаем шаг к пониманию: “это не магия, это сборка платформы”.

Ошибка №3: «Раз у меня одна строчка run(...), то приложение должно стартовать мгновенно».
Маленький код не означает маленький процесс. Boot убирает ручные настройки, но не отменяет необходимость создать контекст, подготовить инфраструктуру и (если это web‑приложение) поднять сервер. Если ожидать “нулевой работы” на старте, вы будете постоянно разочаровываться в любой платформе — не только в Spring.

Ошибка №4: «Я хочу сразу отличать все инфраструктурные бины от своих, но не получается, значит Spring слишком сложный».
Это нормальный этап. Сначала держите фокус на прикладных компонентах: сервисы, репозитории, “bootstrap”-код. Инфраструктура будет «обрастать смыслом» постепенно. Важно не требовать от себя за один день понимания всех внутренних механизмов — иначе вы превратите обучение в спорт по самобичеванию.

Ошибка №5: «Если что-то появилось в контексте, значит это подмешалось “случайно”».
В Boot почти ничего не происходит случайно: это результат подключённых зависимостей и платформенных настроек. Если что-то появилось, у этого есть причина, и она обычно объяснима по classpath, условиям и конфигурации.

1
Задача
Spring Boot, 8 уровень, 0 лекция
Недоступна
Сводка контекста после старта
Сводка контекста после старта
1
Задача
Spring Boot, 8 уровень, 0 лекция
Недоступна
Два своих beans на фоне платформы
Два своих beans на фоне платформы
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ