1. application.yml как часть проекта
Если вы раньше относились к конфигурации как к чему-то вроде «ну да, там где-то лежит файл, который никто не читает», то поздравляю: вы почти в индустрии. Шутка. Но доля правды есть: конфигурацию часто начинают уважать только после первой поломки на ровном месте. В Spring Boot application.yml — это один из основных способов описать runtime-поведение приложения: не то, что делает ваш сервис по бизнес-смыслу, а как он запускается и с какими ограничениями.
В типичном проекте Spring Boot файл лежит здесь:
src/main/resources
└─ application.yml
То есть он попадает в classpath и упаковывается внутрь JAR. Это удобно: проект стартует «из коробки» с предсказуемыми настройками. При этом Spring Boot умеет (и это нормально) получать настройки не только из этого файла, но и из внешних источников. Но сегодня нам важна не вся «вселенная конфигурации», а дисциплина: один базовый файл, единый стиль, понятная структура.
И ещё одна важная мысль: application.yml — это не место для «всяких правил». Если вы вынесете туда правила API-контракта, то однажды вы поменяете YAML в одном окружении, а в другом забудете, и получите два разных API у одного и того же сервиса. Клиенты будут в восторге (нет). Поэтому YAML — для параметров запуска и окружения, а контракт и предметные правила — остаются в коде.
2. YAML как иерархия
YAML выглядит дружелюбно ровно до момента, когда вы забыли пробел. Он похож на текст, но ведёт себя как структура данных. Главная сила YAML в Spring Boot — это возможность представить «длинный ключ с точками» как дерево. Для человека это читается проще: видно, что multipart относится к servlet, servlet относится к spring, и это всё — один смысловой блок.
Например, property-ключ (в «плоском» виде) мог бы выглядеть так:
spring.servlet.multipart.max-file-size=10MB
В YAML он превращается в вложенные уровни:
spring:
# Настройки веб-слоя Spring (всё, что относится к framework-level конфигурации)
servlet:
multipart:
# Максимальный размер одного файла при загрузке
max-file-size: 10MB
С точки зрения Spring Boot это одна и та же настройка. С точки зрения человека — второй вариант гораздо читабельнее: вы буквально видите контекст.
Чтобы «поймать» эту идею, удобно держать в голове маленькую шпаргалку:
| Как выглядит в документации/логике ключей | Как выглядит в application.yml |
|---|---|
|
→ → |
| «длинный ключ с точками» | «вложенные блоки по смыслу» |
И да, про отступы. YAML не любит табы. Вообще. Совсем. Он как строгий тимлид: «табуляция? в моём проекте?». В YAML используйте пробелы, и старайтесь держать единый размер отступа (обычно 2 пробела). Ошибки отступов — это классика жанра: файл выглядит «почти так же», а приложение не стартует.
3. Именование ключей: kebab-case
У Spring Boot есть очень полезная (и иногда опасная) штука: relaxed binding. Это означает, что он умеет сопоставлять разные варианты написания ключей. Условно говоря, storage-dir, storageDir и даже STORAGE_DIR в каком-то контексте могут быть сведены к одной «логической» настройке. Это удобно для интеграции с переменными окружения и разными форматами, но для учебного проекта есть побочный эффект: люди начинают писать ключи «как кому нравится», и конфигурация превращается в смесь диалектов.
В этом курсе мы держим простое правило: в YAML используем kebab-case (через дефис), потому что он:
- визуально стабилен и узнаваем;
- хорошо читается в дереве YAML;
- совпадает с тем, как свойства обычно показывают в примерах Spring Boot документации;
- не провоцирует «а давайте в одном месте camelCase, а в другом — дефисы».
И ещё: консистентность важнее вкуса. Даже если вам субъективно нравится storageDir, в проекте важнее, чтобы все участники (и вы через месяц) понимали конфиг с первого взгляда.
4. Структура конфига: spring и app
Когда конфигурация маленькая, соблазн велик: «накидаю всё в корень, потом разберёмся». А потом оказывается, что «потом» — это никогда, а разбираться приходится на проде (или хотя бы ночью перед демо). Поэтому мы сразу приучаем себя к структуре. В нашем проекте есть две крупные категории настроек: framework-level и application-level, и они должны быть визуально разнесены.
Framework-level — это всё, что относится к Spring и его подсистемам. В рамках нашего дня это, например, включение Problem Details и multipart-ограничения. Эти настройки живут под корнем spring: и дальше идут по иерархии, которую ожидает Boot.
Application-level — это то, что относится именно к нашему приложению, то есть к Task Tracker API. У нас есть настройки вложений, и мы договорились, что они живут под app: (а не под spring:). Например:
app:
# Всё, что относится к нашему приложению, живёт под app.*
attachments:
# Где сохраняем загруженные вложения (путь/директория)
storage-dir: ./data/attachments
Такой подход даёт сразу несколько бонусов. Во-первых, вы не «подмешиваете» свои свойства к чужому неймспейсу spring.* и не рискуете конфликтами. Во-вторых, в конфиге появляется карта проекта: открыв YAML, вы видите «вот Spring-настройки, вот настройки приложения». В-третьих, по мере роста проекта вы сможете добавлять новые feature-блоки под app.* (например, app.api, app.files, app.features) и не превращать корень файла в свалку.
5. Мини-примеры
Сейчас будет самое практичное: маленькие примеры YAML, которые показывают и «как надо», и «как лучше не делать». Старайтесь читать их как мини-истории. Хороший YAML — это когда вы не просто «угадали ключ», а когда по структуре понятно, что именно вы настраиваете и где искать связанные параметры.
Пример 1 — хороший базовый application.yml
spring:
mvc:
problemdetails:
# Включаем Problem Details для более структурированных ошибок
enabled: true
servlet:
multipart:
# Лимит на размер одного загружаемого файла
max-file-size: 10MB
# Лимит на весь запрос целиком (включая метаданные и файл)
max-request-size: 12MB
app:
attachments:
# Директория, куда приложение складывает загруженные вложения
storage-dir: ./data/attachments
Пояснение: конфигурация сразу показывает разделение на framework-настройки и настройки приложения. В одном месте управляем ProblemDetail, в другом — multipart лимитами, а в app.attachments лежит директория хранения вложений. Такой YAML читается как «карта управления» file-сценарием.
Пример 2 — смешение стилей имён
app:
attachments:
# Плохо: camelCase в YAML (у нас договорённость на kebab-case)
storageDir: ./data/attachments
spring:
servlet:
multipart:
# Плохо: camelCase в ключах Spring-настроек (в YAML держим kebab-case)
maxFileSize: 10MB
Пояснение: даже если часть binding-механики это «проглотит», человеку читать неприятно. YAML превращается в смесь стилей. В учебном проекте мы платим не за то, чтобы оно «как-то работало», а за то, чтобы это было понятно и воспроизводимо.
Пример 3 — единый стиль ключей
app:
attachments:
# Хорошо: kebab-case в приложенческих ключах
storage-dir: ./data/attachments
spring:
servlet:
multipart:
# Хорошо: kebab-case в Spring-настройках
max-file-size: 10MB
Пояснение: единый стиль через дефис делает файл визуально ровным. Глаз быстрее находит нужный блок, потому что «язык» конфигурации везде один и тот же.
Пример 4 — комментарии в YAML
app:
attachments:
# Путь до директории, где лежат загруженные файлы.
# Для локального запуска используем относительный путь.
storage-dir: ./data/attachments
Пояснение: комментарий в конфиге — не преступление. Главное, чтобы он объяснял смысл, а не пересказывал очевидное. Через неделю такой комментарий спасает мозг: вы уже не вспоминаете, «почему именно ./data», вы сразу видите намерение.
6. Типичные ошибки при работе с application.yml
Ошибки в конфигурации коварны: код компилируется, тесты могут даже пройти, а приложение ведёт себя странно именно там, где вы меньше всего ждёте. Поэтому полезно заранее знать типовые «грабли». Ниже — самые частые проблемы, которые встречаются в учебных проектах и вполне себе встречаются в рабочих тоже (просто там они дороже).
Ошибка №1: складывают все ключи в корень файла.
Когда application.yml превращается в плоский список «параметр: значение», вы теряете контекст. Через время непонятно, что относится к multipart, что к MVC, а что к приложению. Избежать это проще всего структурой: держите spring: и app: отдельными корнями, а внутри группируйте по подсистемам.
Ошибка №2: кладут свои настройки под spring.*.
Иногда кажется логичным: «ну приложение же Spring, значит всё под spring». Но так вы смешиваете неймспейс фреймворка с неймспейсом приложения и ухудшаете читаемость. В нашем проекте правило простое: всё, что про ваше приложение, живёт под app.*, а всё, что настраивает Spring — под spring.*.
Ошибка №3: используют разный стиль имён в соседних блоках.
Сегодня вы написали storageDir, завтра — storage-dir, послезавтра кто-то добавил maxFileSize, и внезапно конфигурация выглядит как чат, где трое людей спорят о стиле. Даже если Spring Boot умеет это связать, человеку тяжело. Выбирайте один стиль (в нашем курсе — kebab-case) и держите его везде.
Ошибка №4: копируют одинаковые значения в несколько мест.
Дублирование в конфигурации — это как дублирование в коде, только часто хуже: вы можете поменять одно значение и забыть второе, и потом «почему-то не работает». Старайтесь, чтобы у каждого свойства была одна точка правды. Если значение одно и то же — оно должно быть прописано один раз, а не размножено ради спокойствия.
Ошибка №5: ставят machine-specific абсолютный путь как базовый default.
/Users/alex/Downloads/task-tracker выглядит «нормально» на вашем ноутбуке и гарантированно плохо в любой другой среде. Для базового конфига выбирайте переносимые значения, обычно относительные пути (./data/attachments). Абсолютный путь — это уже частный случай конкретного запуска, а не дефолт для репозитория.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ