1. Condition evaluation report: факты
Любая магия в программировании заканчивается ровно в тот момент, когда что-то пошло не так, а дедлайн — пошёл. Spring Boot очень удобен, пока вы довольны результатом. Но как только появляется вопрос «почему оно стартует так», «почему не появился нужный bean», «почему вдруг включилась какая-то подсистема», хочется не философии, а фактов.
Условия и default/backoff уже дали нам правила, но пока это всё видно только в теории. Когда приложение ведёт себя неожиданно, нужен способ прочитать уже принятые решения Boot, а не гадать по логам и собственным догадкам.
Condition evaluation report — это как чек-лист пилота: самолёт (приложение) взлетел, но вы можете посмотреть, какие системы включились, какие — нет, и что именно было сигналом для решения. Без отчёта новичок обычно либо гадает, либо лезет в интернет, либо (классика жанра) пытается «переопределить всё», чтобы стало «как он понимает». Отчёт нужен, чтобы сначала понять, а уже потом что-то менять.
Представьте catalog-service: даже без сложного web-слоя вы уже видите в логах, что поднимается embedded server, что где-то «сам» появился JSON-mapper, а какие-то штуки, наоборот, не включились. Condition report помогает воспринимать это не как «Boot живёт своей жизнью», а как систему правил, которую можно прочитать.
2. Включение отчёта: debug и способы
Condition evaluation report не прячется за секретным рукопожатием. Spring Boot просто не печатает его всегда, потому что вывод получается очень длинным, а консоль — не резиновая. Поэтому режим «покажи всё, что ты решил» включается явно. Хорошая новость: для этого не нужно писать ни одного «специального» класса, это обычная настройка запуска.
Важно сразу зафиксировать ожидание: когда вы включаете debug-режим, вы получаете много текста. Это нормально. И это не «шум», если вы читаете его правильно: не подряд, а отвечая на конкретный вопрос. А ещё debug-режим обычно держат включённым только на время диагностики — иначе в логах будет ощущение, что приложение не стартует, а пишет мемуары.
Ниже — три адекватных способа включить отчёт (и все они сводятся к одному и тому же флагу).
| Способ | Где задаём | Пример |
|---|---|---|
| Через application.yaml | В ресурсах проекта | debug: true |
| Через аргумент запуска | Команда запуска приложения | --debug |
| Программно в main() | В коде CatalogServiceApplication | setDefaultProperties(Map.of("debug","true")) |
Здесь debug нас интересует только как переключатель диагностики старта. Мы не строим вокруг него конфигурационную модель приложения; важно увидеть сам факт: Boot читает этот флаг как обычное свойство и печатает report.
Самый прямолинейный вариант — прописать debug: true в YAML. В учебном проекте это иногда удобно, но держать так постоянно — спорная идея (вы забудете выключить и будете жить в «режиме рассказчика»).
# src/main/resources/application.yaml
# Включаем печать condition evaluation report в логи старта приложения
debug: true
Второй вариант — включать debug флагом запуска. Например, если вы запускаете приложение с аргументами. В Gradle-режиме это тоже возможно, но здесь важно не уходить в детали сборки: достаточно знать, что Boot понимает --debug как «пожалуйста, напечатай condition evaluation report».
Третий вариант — программно. Он хорош именно как учебный, потому что вы видите: это всего лишь свойство, и Boot читает его так же, как любые другие настройки.
import java.util.Map;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class CatalogServiceApplication {
public static void main(String[] args) {
// Создаём SpringApplication вручную, чтобы добавить дефолтные свойства
SpringApplication app = new SpringApplication(CatalogServiceApplication.class);
// То же самое, что debug: true в application.yaml, только задано программно
app.setDefaultProperties(Map.of("debug", "true"));
// Запускаем приложение с переданными аргументами
app.run(args);
}
}
Если вы включили debug, в startup logs появится большой блок, начинающийся примерно как «CONDITIONS EVALUATION REPORT». Это и есть наша цель на сегодня: научиться читать его по-человечески.
3. Структура отчёта: positive и negative
Первое чувство, когда новичок видит condition report, почти всегда одинаковое: «Ого, сколько всего… и половина в negative… значит приложение сломалось?» Нет. Отчёт по своей природе обязан содержать огромное количество negative matches, потому что Spring Boot умеет очень много, а конкретное приложение использует лишь маленькую часть экосистемы.
В нашем catalog-service мы осознанно не подключаем JPA, Security, messaging и прочие штуки. Поэтому «не совпало условие» — это не ошибка, а нормальная констатация: «в этом приложении это не нужно». Более того, отрицательные совпадения — это ваше доказательство того, что Boot не «подмешал» вам случайно лишние подсистемы.
У отчёта довольно стабильная структура. В разных версиях Boot оформление может немного меняться, но смысл разделов одинаковый:
| Раздел | Что означает | Как это читать |
|---|---|---|
| Positive matches | Эти auto-configuration классы включились | Ищем тут, если хотим понять, «почему что-то появилось» |
| Negative matches | Эти auto-configuration классы не включились | Ищем тут, если хотим понять, «почему чего-то нет» |
| Exclusions | Что было исключено явно | В нашем сценарии это почти не трогаем, но раздел можно узнать по имени |
| Unconditional classes | Конфигурации без условий | Они подключаются всегда (или почти всегда) |
Внутри каждого пункта вы увидите примерно такую логику: имя auto-configuration класса и список причин, почему условие совпало или не совпало. Часто там фигурируют формулировки вроде @ConditionalOnClass found required class ... или did not find required class ..., а также условия на наличие/отсутствие beans или на значения properties.
Ниже — короткий, упрощённый пример (не пытайтесь дословно сравнивать со своим выводом, он зависит от версии и набора зависимостей; важна структура).
# Пример фрагмента отчёта (упрощённо): что включилось и почему
Positive matches:
-----------------
DispatcherServletAutoConfiguration matched:
- @ConditionalOnClass found required class 'org.springframework.web.servlet.DispatcherServlet'
- @ConditionalOnWebApplication (Servlet) matched
# И что не включилось, с указанием причины
Negative matches:
-----------------
WebFluxAutoConfiguration:
Did not match:
- @ConditionalOnClass did not find required class 'org.springframework.web.reactive.config.WebFluxConfigurer'
Здесь Boot буквально говорит: «MVC включаю, потому что классы MVC есть и приложение servlet-типа. WebFlux не включаю, потому что классов reactive MVC нет». Никакой мистики — просто проверка условий.
И теперь важная методическая мысль: condition report нельзя читать как роман. Если читать подряд, вы быстро захотите стать фронтендером (да, у нас тут не осуждают). Его читают как справку: «у меня есть вопрос → нахожу релевантный блок → читаю причины».
4. Чтение отчёта на практике
Чтобы condition evaluation report реально помогал, нужна простая дисциплина чтения. Без неё отчёт превращается в ту же «магическую простыню», только ещё длиннее. Хорошая новость: методика очень похожа на нормальную отладку: мы формулируем вопрос, ищем подтверждения, проверяем гипотезы и делаем вывод. Плохая новость: придётся на минуту перестать надеяться на интуицию и начать читать текст.
Ниже мы разберём три очень типовых сценария именно для уровня Boot-курса. Каждый сценарий — это вопрос, который реально возникает у новичка. И в каждом сценарии мы будем действовать одинаково: включаем debug, находим нужную auto-configuration по имени (обычно поиском по логам), читаем причины совпадения/несовпадения, а затем при необходимости сверяемся с фактическим содержимым ApplicationContext.
Когда нужно сверить вывод report с фактическим содержимым контекста, удобны одноразовые probe-компоненты: добавили один, посмотрели факт, убрали. Они не заменяют report, а просто дают второй взгляд на ту же ситуацию.
Сценарий: Tomcat и web-runtime без контроллеров
Когда вы запускаете Boot-приложение с web starter’ом, вы видите в логах что-то вроде «Tomcat started on port 8080». У новичка закономерный вопрос: «Я же не делал ничего вебового… почему оно подняло сервер?» Именно тут report прекрасен: он показывает, какие auto-config включились, чтобы собрать web-runtime.
В отчёте вы будете искать конфигурации, связанные с web server’ом и servlet-стеком. Часто в названии будет «Servlet», «WebServer», «Tomcat». Найдёте — и увидите причины. В здоровом сценарии там будут conditions по classpath (есть классы Tomcat) и по типу приложения (servlet web).
Упрощённый смысл таких строк обычно примерно такой: «класс Tomcat присутствует → значит, можно поднять embedded Tomcat → создаём фабрику web сервера, если пользователь не создал свою».
И вот здесь полезно сделать маленькую проверку фактов: какие именно «инфраструктурные» beans реально живут в контексте. Для этого удобно временно иметь в проекте небольшой ApplicationRunner, который печатает имена beans нужного типа. Это не замена отчёту, а «контрольный звонок»: отчёт говорит, почему что-то должно было включиться, а контекст показывает, что реально получилось.
Это снова временный probe, а не постоянный участник startup-кода.
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
@Component
class WebServerFactoryProbe implements ApplicationRunner {
// Достаём контекст, чтобы посмотреть реальные бины, которые Boot создал на старте
private final ApplicationContext context;
WebServerFactoryProbe(ApplicationContext context) {
this.context = context;
}
@Override
public void run(ApplicationArguments args) {
// Если web starter на classpath, то здесь обычно будет tomcatServletWebServerFactory
var names = context.getBeanNamesForType(ServletWebServerFactory.class);
// Печатаем имена бинов как «контрольный звонок» к condition report
System.out.println("ServletWebServerFactory beans: " + String.join(", ", names));
// ServletWebServerFactory beans: tomcatServletWebServerFactory
}
}
Смысл прост: если web starter на classpath, Boot соберёт web runtime, и в контексте появится ServletWebServerFactory (обычно tomcat-реализация). Если starter убрать — bean исчезнет, и это будет видно и в отчёте (negative match), и в контексте (пустой список).
Здесь фокус не на обработке HTTP-запроса, а на самой платформенной вещи: web runtime — это инфраструктура, и Boot её собирает автоматически по условиям.
Сценарий: Jackson и JSON-инфраструктура из starter’ов
Второй частый сюрприз: через starter приходит Jackson, и у приложения появляется готовая JSON-инфраструктура. У новичка это выглядит так, будто Boot тайком добавил библиотеку. На самом деле всё скучнее (а значит, лучше): Jackson пришёл транзитивно через starter, а auto-configuration подхватила его, потому что классы JSON mapper’а есть на classpath.
В отчёте вы обычно ищете конфигурации, где в названии есть Jackson (например, JacksonAutoConfiguration). В Positive matches будет объяснение в духе: «найден класс JsonMapper, значит можно создать JSON mapper и зарегистрировать связанную инфраструктуру, если её ещё нет».
Чтобы убедиться, что mapper реально в контексте, можно снова «прозвонить» контекст. Это снова временный probe: он нужен только чтобы связать report с фактом.
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import tools.jackson.databind.json.JsonMapper;
@Component
class JsonMapperProbe implements ApplicationRunner {
// Смотрим, какие JsonMapper реально зарегистрированы в ApplicationContext
private final ApplicationContext context;
JsonMapperProbe(ApplicationContext context) {
this.context = context;
}
@Override
public void run(ApplicationArguments args) {
// Если JSON mapper есть на classpath и вы не создали его сами, Boot добавит инфраструктурный бин
var names = context.getBeanNamesForType(JsonMapper.class);
// Выводим имена: это помогает связать «почему включилось» (report) и «что получилось» (контекст)
System.out.println("JSON mapper beans: " + String.join(", ", names));
// Обычно здесь увидите хотя бы один bean JSON mapper’а
}
}
Очень важно не сделать неверный вывод. Наличие JSON mapper’а не значит, что вам срочно нужно его «переопределить» или «настроить правильно». Здесь важно другое: Boot включил его закономерно, потому что совпали условия. Точное имя бина сейчас несущественно; важен сам факт его появления как инфраструктурного default.
Сценарий: JPA, Security и WebFlux не включились — и это хорошо
Третий сценарий психологически самый важный. Вы включаете debug, видите гигантский раздел Negative matches и начинаете подозревать, что приложение «не до конца собралось». На самом деле это означает противоположное: приложение собралось ровно так, как нужно, и Boot не включил лишнего.
Для catalog-service это особенно принципиально. Это read-only сервис без БД, без Security и без reactive стека. Поэтому в отчёте вы должны увидеть отрицательные совпадения для конфигураций, связанных с JPA, Security и WebFlux. Причины обычно простые: нет нужных классов на classpath (потому что нет starter’а), не выполнены условия по типу приложения, нет нужных bean’ов.
Упрощённый «читаемый» пример мысли Boot выглядит так: «в проекте нет spring-boot-starter-data-jpa, значит нет классов EntityManager и друзей → auto-configuration JPA не включаем». Или: «нет spring-boot-starter-security, значит нет нужных классов Security → не включаем security auto-config».
И вот тут возникает полезная привычка: negative match — это доказательство, что вас не утащило в соседнюю технологию. Это не «что-то сломалось», это «условия не выполнены». Иногда это самое приятное место отчёта, потому что оно подтверждает: проект остаётся в границах.
Если хочется «потрогать руками», можно найти в отчёте одну-две строки, например про WebFlux, и прочитать причины. Они почти всегда будут вида «did not find required class ...». То есть Boot буквально говорит: «у тебя этого нет на classpath, поэтому я это не включаю». И это ровно то, что мы хотим видеть в базовом Boot-template, который не превращается в витрину всего Spring сразу.
Маленькие приёмы, чтобы отчёт не стал наказанием
Condition evaluation report — инструмент сильный, но он не пытается быть «красивым». Он честный. И поэтому есть несколько практичных приёмов, которые резко повышают его полезность, особенно для начинающего. Во-первых, всегда начинайте с вопроса в формате «почему X включилось?» или «почему Y не включилось?», а не с попытки понять весь Boot за один запуск. Во-вторых, ищите в отчёте по ключевым словам: имя auto-configuration класса, название технологии, иногда — имя класса, которого «не нашли».
Ещё одна рабочая привычка — сверять отчёт с фактом контекста. Отчёт показывает причины решения, но если вы хотите убедиться, что бин действительно создан, ApplicationContext даст прямой ответ. В этом смысле маленькие probe-компоненты (как в примерах выше) — это «фонарик»: вы не превращаете проект в диагностическую лабораторию, вы просто на минуту включаете свет и проверяете, что в комнате действительно стоит тот шкаф, про который вам рассказали.
Наконец, помните, что в отчёте много технических имён, и это нормально. Вам не нужно «знать все auto-config классы». Достаточно узнавать паттерны: OnClassCondition почти всегда про classpath, OnPropertyCondition — про свойства, OnBeanCondition и OnMissingBeanCondition — про наличие/отсутствие bean’ов. Через пару запусков эти слова перестают пугать и начинают работать как дорожные знаки: не красиво, но понятно.
5. Типичные ошибки при работе с condition evaluation report
Когда вы впервые включаете debug=true, очень легко сделать несколько типичных «новичковых» выводов, которые звучат логично, но ведут не туда. Самая частая проблема — пытаться читать отчёт как единый текст и переживать из‑за каждого negative match. Вторая — делать выводы по одному совпадению, не проверяя, что в итоге действительно создалось. Ниже — ошибки, которые встречаются чаще всего и больнее всего мешают учиться.
Ошибка №1: включить debug и пытаться прочитать отчёт сверху вниз, не имея вопроса.
Так вы получите только одно: усталость и желание спрятаться под столом вместе с котом. Отчёт полезен, когда вы идёте от конкретного «почему?» и ищете конкретный блок. Тогда даже огромный вывод превращается в справочник, а не в поток сознания Spring Boot.
Ошибка №2: воспринимать negative match как признак поломки приложения.
У нормального приложения negative matches всегда будет много, потому что Boot умеет больше, чем нужно вашему проекту. Для catalog-service отрицательные совпадения по JPA, Security и WebFlux — это вообще хороший признак: это подтверждение, что вы не утащили проект в чужую предметную область.
Ошибка №3: думать, что positive match автоматически означает «всё готово и работает».
Positive match говорит только одно: auto-configuration класс включился. Но итоговый эффект может зависеть от других условий и от того, какие beans реально создались. Поэтому полезно иногда сверяться с ApplicationContext: если вы ожидаете ServletWebServerFactory — проверьте, что он есть; если ожидаете JSON mapper — проверьте, что он действительно зарегистрирован.
Ошибка №4: игнорировать причины внутри negative match и застревать на заголовке.
Часто новичок видит название конфигурации и делает вывод «не включилось, значит плохо». Но смысл отчёта в причинах. Одна строчка «did not find required class ...» моментально объясняет, что это просто отсутствующий starter, а не загадочный баг в контейнере.
Ошибка №5: пытаться «починить» отчёт кастомизацией, не поняв причин.
Самый опасный путь — сразу лезть переопределять beans, добавлять аннотации и выключать auto-configuration, потому что «в отчёте что-то не нравится». Отчёт — это не список того, что надо срочно поменять. Это объяснение текущего поведения. Сначала читаем, понимаем, и только потом (если реально нужно) меняем приложение.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ